<!DOCTYPE HTML>
<html lang="Chinese">


<head>
    <meta charset="utf-8">
    <meta name="keywords" content="分布式搜索引擎下, 博客">
    <meta name="description" content="In me the tiger sniffs the rose.">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
    <meta name="renderer" content="webkit|ie-stand|ie-comp">
    <meta name="mobile-web-app-capable" content="yes">
    <meta name="format-detection" content="telephone=no">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
    <!-- Global site tag (gtag.js) - Google Analytics -->


    <title>分布式搜索引擎下 | 凡诚</title>
    <link rel="icon" type="image/png" href="/favicon.png">

    <link rel="stylesheet" type="text/css" href="/libs/awesome/css/all.css">
    <link rel="stylesheet" type="text/css" href="/libs/materialize/materialize.min.css">
    <link rel="stylesheet" type="text/css" href="/libs/aos/aos.css">
    <link rel="stylesheet" type="text/css" href="/libs/animate/animate.min.css">
    <link rel="stylesheet" type="text/css" href="/libs/lightGallery/css/lightgallery.min.css">
    <link rel="stylesheet" type="text/css" href="/css/matery.css">
    <link rel="stylesheet" type="text/css" href="/css/my.css">
    <script src="https://sdk.jinrishici.com/v2/browser/jinrishici.js" charset="utf-8"></script>
    <script src="/libs/jquery/jquery.min.js"></script>

<meta name="generator" content="Hexo 5.4.2"></head>



   <style>
    body{
       background-image: url(https://cdn.jsdelivr.net/gh/Tokisaki-Galaxy/res/site/medias/background.jpg);
       background-repeat:no-repeat;
       background-size:cover;
       background-attachment:fixed;
    }
</style>



<body>
    <header class="navbar-fixed">
    <nav id="headNav" class="bg-color nav-transparent">
        <div id="navContainer" class="nav-wrapper container">
            <div class="brand-logo">
                <a href="/" class="waves-effect waves-light">
                    
                    <img src="/medias/comment_bg.png" class="logo-img" alt="LOGO">
                    
                    <span class="logo-span">凡诚</span>
                </a>
            </div>
            

<a href="#" data-target="mobile-nav" class="sidenav-trigger button-collapse"><i class="fas fa-bars"></i></a>
<ul class="right nav-menu">
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/" class="waves-effect waves-light">
      
      <i class="fas fa-home" style="zoom: 0.6;"></i>
      
      <span>主页</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/tags" class="waves-effect waves-light">
      
      <i class="fas fa-tags" style="zoom: 0.6;"></i>
      
      <span>文章</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/categories" class="waves-effect waves-light">
      
      <i class="fas fa-bookmark" style="zoom: 0.6;"></i>
      
      <span>分类</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/archives" class="waves-effect waves-light">
      
      <i class="fas fa-archive" style="zoom: 0.6;"></i>
      
      <span>归档</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="" class="waves-effect waves-light">

      
      <i class="fas fa-user-circle" style="zoom: 0.6;"></i>
      
      <span>关于我</span>
      <i class="fas fa-chevron-down" aria-hidden="true" style="zoom: 0.6;"></i>
    </a>
    <ul class="sub-nav menus_item_child ">
      
      <li>
        <a href="/about">
          
          <i class="fas fa-user-circle" style="margin-top: -20px; zoom: 0.6;"></i>
          
          <span>我的信息</span>
        </a>
      </li>
      
      <li>
        <a href="/contact">
          
          <i class="fas fa-comments" style="margin-top: -20px; zoom: 0.6;"></i>
          
          <span>留言板</span>
        </a>
      </li>
      
      <li>
        <a href="/friends">
          
          <i class="fas fa-address-book" style="margin-top: -20px; zoom: 0.6;"></i>
          
          <span>友链</span>
        </a>
      </li>
      
    </ul>
    
  </li>
  
  <li>
    <a href="#searchModal" class="modal-trigger waves-effect waves-light">
      <i id="searchIcon" class="fas fa-search" title="Search" style="zoom: 0.85;"></i>
    </a>
  </li>
</ul>


<div id="mobile-nav" class="side-nav sidenav">

    <div class="mobile-head bg-color">
        
        <img src="/medias/comment_bg.png" class="logo-img circle responsive-img">
        
        <div class="logo-name">凡诚</div>
        <div class="logo-desc">
            
            In me the tiger sniffs the rose.
            
        </div>
    </div>

    

    <ul class="menu-list mobile-menu-list">
        
        <li class="m-nav-item">
	  
		<a href="/" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-home"></i>
			
			主页
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="/tags" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-tags"></i>
			
			文章
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="/categories" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-bookmark"></i>
			
			分类
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="/archives" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-archive"></i>
			
			归档
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="javascript:;">
			
				<i class="fa-fw fas fa-user-circle"></i>
			
			关于我
			<span class="m-icon"><i class="fas fa-chevron-right"></i></span>
		</a>
            <ul  style="background:  ;" >
              
                <li>

                  <a href="/about " style="margin-left:75px">
				  
				   <i class="fa fas fa-user-circle" style="position: absolute;left:50px" ></i>
			      
		          <span>我的信息</span>
                  </a>
                </li>
              
                <li>

                  <a href="/contact " style="margin-left:75px">
				  
				   <i class="fa fas fa-comments" style="position: absolute;left:50px" ></i>
			      
		          <span>留言板</span>
                  </a>
                </li>
              
                <li>

                  <a href="/friends " style="margin-left:75px">
				  
				   <i class="fa fas fa-address-book" style="position: absolute;left:50px" ></i>
			      
		          <span>友链</span>
                  </a>
                </li>
              
            </ul>
          
        </li>
        
        
    </ul>
</div>


        </div>

        
    </nav>

</header>

    



<div class="bg-cover pd-header post-cover" style="background-image: url('/medias/featureimages/2.jpg')">
    <div class="container" style="right: 0px;left: 0px;">
        <div class="row">
            <div class="col s12 m12 l12">
                <div class="brand">
                    <h1 class="description center-align post-title">分布式搜索引擎下</h1>
                </div>
            </div>
        </div>
    </div>
</div>




<main class="post-container content">

    
    <link rel="stylesheet" href="/libs/tocbot/tocbot.css">
<style>
    #articleContent h1::before,
    #articleContent h2::before,
    #articleContent h3::before,
    #articleContent h4::before,
    #articleContent h5::before,
    #articleContent h6::before {
        display: block;
        content: " ";
        height: 100px;
        margin-top: -100px;
        visibility: hidden;
    }

    #articleContent :focus {
        outline: none;
    }

    .toc-fixed {
        position: fixed;
        top: 64px;
    }

    .toc-widget {
        width: 345px;
        padding-left: 20px;
    }

    .toc-widget .toc-title {
        padding: 35px 0 15px 17px;
        font-size: 1.5rem;
        font-weight: bold;
        line-height: 1.5rem;
    }

    .toc-widget ol {
        padding: 0;
        list-style: none;
    }

    #toc-content {
        padding-bottom: 30px;
        overflow: auto;
    }

    #toc-content ol {
        padding-left: 10px;
    }

    #toc-content ol li {
        padding-left: 10px;
    }

    #toc-content .toc-link:hover {
        color: #42b983;
        font-weight: 700;
        text-decoration: underline;
    }

    #toc-content .toc-link::before {
        background-color: transparent;
        max-height: 25px;

        position: absolute;
        right: 23.5vw;
        display: block;
    }

    #toc-content .is-active-link {
        color: #42b983;
    }

    #floating-toc-btn {
        position: fixed;
        right: 15px;
        bottom: 76px;
        padding-top: 15px;
        margin-bottom: 0;
        z-index: 998;
    }

    #floating-toc-btn .btn-floating {
        width: 48px;
        height: 48px;
    }

    #floating-toc-btn .btn-floating i {
        line-height: 48px;
        font-size: 1.4rem;
    }
</style>
<div class="row">
    <div id="main-content" class="col s12 m12 l9">
        <!-- 文章内容详情 -->
<div id="artDetail">
    <div class="card">
        <div class="card-content article-info">
            <div class="row tag-cate">
                <div class="col s7">
                    
                    <div class="article-tag">
                        
                            <a href="/tags/java/">
                                <span class="chip bg-color">java</span>
                            </a>
                        
                            <a href="/tags/spring/">
                                <span class="chip bg-color">spring</span>
                            </a>
                        
                            <a href="/tags/springBoot/">
                                <span class="chip bg-color">springBoot</span>
                            </a>
                        
                            <a href="/tags/%E5%BE%AE%E6%9C%8D%E5%8A%A1/">
                                <span class="chip bg-color">微服务</span>
                            </a>
                        
                            <a href="/tags/%E5%88%86%E5%B8%83%E5%BC%8F/">
                                <span class="chip bg-color">分布式</span>
                            </a>
                        
                            <a href="/tags/springCloud/">
                                <span class="chip bg-color">springCloud</span>
                            </a>
                        
                            <a href="/tags/elasticSearch/">
                                <span class="chip bg-color">elasticSearch</span>
                            </a>
                        
                    </div>
                    
                </div>
                <div class="col s5 right-align">
                    
                    <div class="post-cate">
                        <i class="fas fa-bookmark fa-fw icon-category"></i>
                        
                            <a href="/categories/%E5%BE%AE%E6%9C%8D%E5%8A%A1%E6%8A%80%E6%9C%AF/" class="post-category">
                                微服务技术
                            </a>
                        
                    </div>
                    
                </div>
            </div>

            <div class="post-info">
                
                <div class="post-date info-break-policy">
                    <i class="far fa-calendar-minus fa-fw"></i>Publish Date:&nbsp;&nbsp;
                    2022-08-29
                </div>
                

                
                <div class="post-date info-break-policy">
                    <i class="far fa-calendar-check fa-fw"></i>Update Date:&nbsp;&nbsp;
                    2022-11-09
                </div>
                

                
                <div class="info-break-policy">
                    <i class="far fa-file-word fa-fw"></i>Word Count:&nbsp;&nbsp;
                    6.4k
                </div>
                

                
                <div class="info-break-policy">
                    <i class="far fa-clock fa-fw"></i>Read Times:&nbsp;&nbsp;
                    26 Min
                </div>
                

                
                    <div id="busuanzi_container_page_pv" class="info-break-policy">
                        <i class="far fa-eye fa-fw"></i>Read Count:&nbsp;&nbsp;
                        <span id="busuanzi_value_page_pv"></span>
                    </div>
				
            </div>
        </div>
        <hr class="clearfix">

        
        <!-- 是否加载使用自带的 prismjs. -->
        <link rel="stylesheet" href="/libs/prism/prism.css">
        

        

        <div class="card-content article-card-content">
            <div id="articleContent">
                <h1 id="分布式搜索引擎"><a href="#分布式搜索引擎" class="headerlink" title="分布式搜索引擎"></a>分布式搜索引擎</h1><h1 id="0-学习目标"><a href="#0-学习目标" class="headerlink" title="0.学习目标"></a>0.学习目标</h1><h1 id="1-数据聚合"><a href="#1-数据聚合" class="headerlink" title="1.数据聚合"></a>1.数据聚合</h1><p>**<a target="_blank" rel="noopener" href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations.html">聚合（</a><a target="_blank" rel="noopener" href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations.html">aggregations</a><a target="_blank" rel="noopener" href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations.html">）</a>**可以让我们极其方便的实现对数据的统计、分析、运算。例如：</p>
<ul>
<li>什么品牌的手机最受欢迎？</li>
<li>这些手机的平均价格、最高价格、最低价格？</li>
<li>这些手机每月的销售情况如何？</li>
</ul>
<p>实现这些统计功能的比数据库的sql要方便的多，而且查询速度非常快，可以实现近实时搜索效果。</p>
<h2 id="1-1-聚合的种类"><a href="#1-1-聚合的种类" class="headerlink" title="1.1.聚合的种类"></a>1.1.聚合的种类</h2><p>聚合常见的有三类：</p>
<ul>
<li><p><strong>桶（Bucket）</strong>聚合：用来对文档做分组</p>
<ul>
<li>TermAggregation：按照文档字段值分组，例如按照品牌值分组、按照国家分组</li>
<li>Date Histogram：按照日期阶梯分组，例如一周为一组，或者一月为一组</li>
</ul>
</li>
<li><p><strong>度量（Metric）</strong>聚合：用以计算一些值，比如：最大值、最小值、平均值等</p>
<ul>
<li>Avg：求平均值</li>
<li>Max：求最大值</li>
<li>Min：求最小值</li>
<li>Stats：同时求max、min、avg、sum等</li>
</ul>
</li>
<li><p><strong>管道（pipeline）</strong>聚合：其它聚合的结果为基础做聚合</p>
</li>
</ul>
<blockquote>
<p><strong>注意：</strong>参加聚合的字段必须是keyword、日期、数值、布尔类型</p>
</blockquote>
<h2 id="1-2-DSL实现聚合"><a href="#1-2-DSL实现聚合" class="headerlink" title="1.2.DSL实现聚合"></a>1.2.DSL实现聚合</h2><p>现在，我们要统计所有数据中的酒店品牌有几种，其实就是按照品牌对数据分组。此时可以根据酒店品牌的名称做聚合，也就是Bucket聚合。</p>
<h3 id="1-2-1-Bucket聚合语法"><a href="#1-2-1-Bucket聚合语法" class="headerlink" title="1.2.1.Bucket聚合语法"></a>1.2.1.Bucket聚合语法</h3><p>语法如下：</p>
<pre class="line-numbers language-json" data-language="json"><code class="language-json">GET /hotel/_search
<span class="token punctuation">&#123;</span>
  <span class="token property">"size"</span><span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span>  <span class="token comment">// 设置size为0，结果中不包含文档，只包含聚合结果</span>
  <span class="token property">"aggs"</span><span class="token operator">:</span> <span class="token punctuation">&#123;</span> <span class="token comment">// 定义聚合</span>
    <span class="token property">"brandAgg"</span><span class="token operator">:</span> <span class="token punctuation">&#123;</span> <span class="token comment">//给聚合起个名字</span>
      <span class="token property">"terms"</span><span class="token operator">:</span> <span class="token punctuation">&#123;</span> <span class="token comment">// 聚合的类型，按照品牌值聚合，所以选择term</span>
        <span class="token property">"field"</span><span class="token operator">:</span> <span class="token string">"brand"</span><span class="token punctuation">,</span> <span class="token comment">// 参与聚合的字段</span>
        <span class="token property">"size"</span><span class="token operator">:</span> <span class="token number">20</span> <span class="token comment">// 希望获取的聚合结果数量</span>
      <span class="token punctuation">&#125;</span>
    <span class="token punctuation">&#125;</span>
  <span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<p>结果如图：</p>
<p><img src="assets/image-20210723171948228.png" alt="image-20210723171948228"></p>
<h3 id="1-2-2-聚合结果排序"><a href="#1-2-2-聚合结果排序" class="headerlink" title="1.2.2.聚合结果排序"></a>1.2.2.聚合结果排序</h3><p>默认情况下，Bucket聚合会统计Bucket内的文档数量，记为_count，并且按照_count降序排序。</p>
<p>我们可以指定order属性，自定义聚合的排序方式：</p>
<pre class="line-numbers language-json" data-language="json"><code class="language-json">GET /hotel/_search
<span class="token punctuation">&#123;</span>
  <span class="token property">"size"</span><span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span> 
  <span class="token property">"aggs"</span><span class="token operator">:</span> <span class="token punctuation">&#123;</span>
    <span class="token property">"brandAgg"</span><span class="token operator">:</span> <span class="token punctuation">&#123;</span>
      <span class="token property">"terms"</span><span class="token operator">:</span> <span class="token punctuation">&#123;</span>
        <span class="token property">"field"</span><span class="token operator">:</span> <span class="token string">"brand"</span><span class="token punctuation">,</span>
        <span class="token property">"order"</span><span class="token operator">:</span> <span class="token punctuation">&#123;</span>
          <span class="token property">"_count"</span><span class="token operator">:</span> <span class="token string">"asc"</span> <span class="token comment">// 按照_count升序排列</span>
        <span class="token punctuation">&#125;</span><span class="token punctuation">,</span>
        <span class="token property">"size"</span><span class="token operator">:</span> <span class="token number">20</span>
      <span class="token punctuation">&#125;</span>
    <span class="token punctuation">&#125;</span>
  <span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>



<h3 id="1-2-3-限定聚合范围"><a href="#1-2-3-限定聚合范围" class="headerlink" title="1.2.3.限定聚合范围"></a>1.2.3.限定聚合范围</h3><p>默认情况下，Bucket聚合是对索引库的所有文档做聚合，但真实场景下，用户会输入搜索条件，因此聚合必须是对搜索结果聚合。那么聚合必须添加限定条件。</p>
<p>我们可以限定要聚合的文档范围，只要添加query条件即可：</p>
<pre class="line-numbers language-json" data-language="json"><code class="language-json">GET /hotel/_search
<span class="token punctuation">&#123;</span>
  <span class="token property">"query"</span><span class="token operator">:</span> <span class="token punctuation">&#123;</span>
    <span class="token property">"range"</span><span class="token operator">:</span> <span class="token punctuation">&#123;</span>
      <span class="token property">"price"</span><span class="token operator">:</span> <span class="token punctuation">&#123;</span>
        <span class="token property">"lte"</span><span class="token operator">:</span> <span class="token number">200</span> <span class="token comment">// 只对200元以下的文档聚合</span>
      <span class="token punctuation">&#125;</span>
    <span class="token punctuation">&#125;</span>
  <span class="token punctuation">&#125;</span><span class="token punctuation">,</span> 
  <span class="token property">"size"</span><span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span> 
  <span class="token property">"aggs"</span><span class="token operator">:</span> <span class="token punctuation">&#123;</span>
    <span class="token property">"brandAgg"</span><span class="token operator">:</span> <span class="token punctuation">&#123;</span>
      <span class="token property">"terms"</span><span class="token operator">:</span> <span class="token punctuation">&#123;</span>
        <span class="token property">"field"</span><span class="token operator">:</span> <span class="token string">"brand"</span><span class="token punctuation">,</span>
        <span class="token property">"size"</span><span class="token operator">:</span> <span class="token number">20</span>
      <span class="token punctuation">&#125;</span>
    <span class="token punctuation">&#125;</span>
  <span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>



<p>这次，聚合得到的品牌明显变少了：</p>
<p><img src="assets/image-20210723172404836.png" alt="image-20210723172404836"></p>
<h3 id="1-2-4-Metric聚合语法"><a href="#1-2-4-Metric聚合语法" class="headerlink" title="1.2.4.Metric聚合语法"></a>1.2.4.Metric聚合语法</h3><p>上节课，我们对酒店按照品牌分组，形成了一个个桶。现在我们需要对桶内的酒店做运算，获取每个品牌的用户评分的min、max、avg等值。</p>
<p>这就要用到Metric聚合了，例如stat聚合：就可以获取min、max、avg等结果。</p>
<p>语法如下：</p>
<pre class="line-numbers language-json" data-language="json"><code class="language-json">GET /hotel/_search
<span class="token punctuation">&#123;</span>
  <span class="token property">"size"</span><span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span> 
  <span class="token property">"aggs"</span><span class="token operator">:</span> <span class="token punctuation">&#123;</span>
    <span class="token property">"brandAgg"</span><span class="token operator">:</span> <span class="token punctuation">&#123;</span> 
      <span class="token property">"terms"</span><span class="token operator">:</span> <span class="token punctuation">&#123;</span> 
        <span class="token property">"field"</span><span class="token operator">:</span> <span class="token string">"brand"</span><span class="token punctuation">,</span> 
        <span class="token property">"size"</span><span class="token operator">:</span> <span class="token number">20</span>
      <span class="token punctuation">&#125;</span><span class="token punctuation">,</span>
      <span class="token property">"aggs"</span><span class="token operator">:</span> <span class="token punctuation">&#123;</span> <span class="token comment">// 是brands聚合的子聚合，也就是分组后对每组分别计算</span>
        <span class="token property">"score_stats"</span><span class="token operator">:</span> <span class="token punctuation">&#123;</span> <span class="token comment">// 聚合名称</span>
          <span class="token property">"stats"</span><span class="token operator">:</span> <span class="token punctuation">&#123;</span> <span class="token comment">// 聚合类型，这里stats可以计算min、max、avg等</span>
            <span class="token property">"field"</span><span class="token operator">:</span> <span class="token string">"score"</span> <span class="token comment">// 聚合字段，这里是score</span>
          <span class="token punctuation">&#125;</span>
        <span class="token punctuation">&#125;</span>
      <span class="token punctuation">&#125;</span>
    <span class="token punctuation">&#125;</span>
  <span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>



<p>这次的score_stats聚合是在brandAgg的聚合内部嵌套的子聚合。因为我们需要在每个桶分别计算。</p>
<p>另外，我们还可以给聚合结果做个排序，例如按照每个桶的酒店平均分做排序：</p>
<p><img src="assets/image-20210723172917636.png" alt="image-20210723172917636"></p>
<h3 id="1-2-5-小结"><a href="#1-2-5-小结" class="headerlink" title="1.2.5.小结"></a>1.2.5.小结</h3><p>aggs代表聚合，与query同级，此时query的作用是？</p>
<ul>
<li>限定聚合的的文档范围</li>
</ul>
<p>聚合必须的三要素：</p>
<ul>
<li>聚合名称</li>
<li>聚合类型</li>
<li>聚合字段</li>
</ul>
<p>聚合可配置属性有：</p>
<ul>
<li>size：指定聚合结果数量</li>
<li>order：指定聚合结果排序方式</li>
<li>field：指定聚合字段</li>
</ul>
<h2 id="1-3-RestAPI实现聚合"><a href="#1-3-RestAPI实现聚合" class="headerlink" title="1.3.RestAPI实现聚合"></a>1.3.RestAPI实现聚合</h2><h3 id="1-3-1-API语法"><a href="#1-3-1-API语法" class="headerlink" title="1.3.1.API语法"></a>1.3.1.API语法</h3><p>聚合条件与query条件同级别，因此需要使用request.source()来指定聚合条件。</p>
<p>聚合条件的语法：</p>
<p><img src="assets/image-20210723173057733.png" alt="image-20210723173057733"></p>
<p>聚合的结果也与查询结果不同，API也比较特殊。不过同样是JSON逐层解析：</p>
<p><img src="assets/image-20210723173215728.png" alt="image-20210723173215728"></p>
<h3 id="1-3-2-业务需求"><a href="#1-3-2-业务需求" class="headerlink" title="1.3.2.业务需求"></a>1.3.2.业务需求</h3><p>需求：搜索页面的品牌、城市等信息不应该是在页面写死，而是通过聚合索引库中的酒店数据得来的：</p>
<p><img src="assets/image-20210723192605566.png" alt="image-20210723192605566"></p>
<p>分析：</p>
<p>目前，页面的城市列表、星级列表、品牌列表都是写死的，并不会随着搜索结果的变化而变化。但是用户搜索条件改变时，搜索结果会跟着变化。</p>
<p>例如：用户搜索“东方明珠”，那搜索的酒店肯定是在上海东方明珠附近，因此，城市只能是上海，此时城市列表中就不应该显示北京、深圳、杭州这些信息了。</p>
<p>也就是说，搜索结果中包含哪些城市，页面就应该列出哪些城市；搜索结果中包含哪些品牌，页面就应该列出哪些品牌。</p>
<p>如何得知搜索结果中包含哪些品牌？如何得知搜索结果中包含哪些城市？</p>
<p>使用聚合功能，利用Bucket聚合，对搜索结果中的文档基于品牌分组、基于城市分组，就能得知包含哪些品牌、哪些城市了。</p>
<p>因为是对搜索结果聚合，因此聚合是<strong>限定范围的聚合</strong>，也就是说聚合的限定条件跟搜索文档的条件一致。</p>
<p>查看浏览器可以发现，前端其实已经发出了这样的一个请求：</p>
<p><img src="assets/image-20210723193730799.png" alt="image-20210723193730799"></p>
<p>请求<strong>参数与搜索文档的参数完全一致</strong>。</p>
<p>返回值类型就是页面要展示的最终结果：</p>
<p><img src="assets/image-20210723203915982.png" alt="image-20210723203915982"></p>
<p>结果是一个Map结构：</p>
<ul>
<li>key是字符串，城市、星级、品牌、价格</li>
<li>value是集合，例如多个城市的名称</li>
</ul>
<h3 id="1-3-3-业务实现"><a href="#1-3-3-业务实现" class="headerlink" title="1.3.3.业务实现"></a>1.3.3.业务实现</h3><p>在<code>cn.itcast.hotel.web</code>包的<code>HotelController</code>中添加一个方法，遵循下面的要求：</p>
<ul>
<li>请求方式：<code>POST</code></li>
<li>请求路径：<code>/hotel/filters</code></li>
<li>请求参数：<code>RequestParams</code>，与搜索文档的参数一致</li>
<li>返回值类型：<code>Map&lt;String, List&lt;String&gt;&gt;</code></li>
</ul>
<p>代码：</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token annotation punctuation">@PostMapping</span><span class="token punctuation">(</span><span class="token string">"filters"</span><span class="token punctuation">)</span>
<span class="token keyword">public</span> <span class="token class-name">Map</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">String</span><span class="token punctuation">,</span> <span class="token class-name">List</span><span class="token punctuation">&lt;</span><span class="token class-name">String</span><span class="token punctuation">></span><span class="token punctuation">></span></span> <span class="token function">getFilters</span><span class="token punctuation">(</span><span class="token annotation punctuation">@RequestBody</span> <span class="token class-name">RequestParams</span> params<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>
    <span class="token keyword">return</span> hotelService<span class="token punctuation">.</span><span class="token function">getFilters</span><span class="token punctuation">(</span>params<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre>



<p>这里调用了IHotelService中的getFilters方法，尚未实现。</p>
<p>在<code>cn.itcast.hotel.service.IHotelService</code>中定义新方法：</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token class-name">Map</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">String</span><span class="token punctuation">,</span> <span class="token class-name">List</span><span class="token punctuation">&lt;</span><span class="token class-name">String</span><span class="token punctuation">></span><span class="token punctuation">></span></span> <span class="token function">filters</span><span class="token punctuation">(</span><span class="token class-name">RequestParams</span> params<span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre>



<p>在<code>cn.itcast.hotel.service.impl.HotelService</code>中实现该方法：</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token annotation punctuation">@Override</span>
<span class="token keyword">public</span> <span class="token class-name">Map</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">String</span><span class="token punctuation">,</span> <span class="token class-name">List</span><span class="token punctuation">&lt;</span><span class="token class-name">String</span><span class="token punctuation">></span><span class="token punctuation">></span></span> <span class="token function">filters</span><span class="token punctuation">(</span><span class="token class-name">RequestParams</span> params<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    <span class="token keyword">try</span> <span class="token punctuation">&#123;</span>
        <span class="token comment">// 1.准备Request</span>
        <span class="token class-name">SearchRequest</span> request <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">SearchRequest</span><span class="token punctuation">(</span><span class="token string">"hotel"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 2.准备DSL</span>
        <span class="token comment">// 2.1.query</span>
        <span class="token function">buildBasicQuery</span><span class="token punctuation">(</span>params<span class="token punctuation">,</span> request<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 2.2.设置size</span>
        request<span class="token punctuation">.</span><span class="token function">source</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">size</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 2.3.聚合</span>
        <span class="token function">buildAggregation</span><span class="token punctuation">(</span>request<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 3.发出请求</span>
        <span class="token class-name">SearchResponse</span> response <span class="token operator">=</span> client<span class="token punctuation">.</span><span class="token function">search</span><span class="token punctuation">(</span>request<span class="token punctuation">,</span> <span class="token class-name">RequestOptions</span><span class="token punctuation">.</span><span class="token constant">DEFAULT</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 4.解析结果</span>
        <span class="token class-name">Map</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">String</span><span class="token punctuation">,</span> <span class="token class-name">List</span><span class="token punctuation">&lt;</span><span class="token class-name">String</span><span class="token punctuation">></span><span class="token punctuation">></span></span> result <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">HashMap</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token class-name">Aggregations</span> aggregations <span class="token operator">=</span> response<span class="token punctuation">.</span><span class="token function">getAggregations</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 4.1.根据品牌名称，获取品牌结果</span>
        <span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">String</span><span class="token punctuation">></span></span> brandList <span class="token operator">=</span> <span class="token function">getAggByName</span><span class="token punctuation">(</span>aggregations<span class="token punctuation">,</span> <span class="token string">"brandAgg"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        result<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span><span class="token string">"品牌"</span><span class="token punctuation">,</span> brandList<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 4.2.根据品牌名称，获取品牌结果</span>
        <span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">String</span><span class="token punctuation">></span></span> cityList <span class="token operator">=</span> <span class="token function">getAggByName</span><span class="token punctuation">(</span>aggregations<span class="token punctuation">,</span> <span class="token string">"cityAgg"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        result<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span><span class="token string">"城市"</span><span class="token punctuation">,</span> cityList<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 4.3.根据品牌名称，获取品牌结果</span>
        <span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">String</span><span class="token punctuation">></span></span> starList <span class="token operator">=</span> <span class="token function">getAggByName</span><span class="token punctuation">(</span>aggregations<span class="token punctuation">,</span> <span class="token string">"starAgg"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        result<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span><span class="token string">"星级"</span><span class="token punctuation">,</span> starList<span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">return</span> result<span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">IOException</span> e<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">RuntimeException</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span>

<span class="token keyword">private</span> <span class="token keyword">void</span> <span class="token function">buildAggregation</span><span class="token punctuation">(</span><span class="token class-name">SearchRequest</span> request<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    request<span class="token punctuation">.</span><span class="token function">source</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">aggregation</span><span class="token punctuation">(</span><span class="token class-name">AggregationBuilders</span>
                                 <span class="token punctuation">.</span><span class="token function">terms</span><span class="token punctuation">(</span><span class="token string">"brandAgg"</span><span class="token punctuation">)</span>
                                 <span class="token punctuation">.</span><span class="token function">field</span><span class="token punctuation">(</span><span class="token string">"brand"</span><span class="token punctuation">)</span>
                                 <span class="token punctuation">.</span><span class="token function">size</span><span class="token punctuation">(</span><span class="token number">100</span><span class="token punctuation">)</span>
                                <span class="token punctuation">)</span><span class="token punctuation">;</span>
    request<span class="token punctuation">.</span><span class="token function">source</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">aggregation</span><span class="token punctuation">(</span><span class="token class-name">AggregationBuilders</span>
                                 <span class="token punctuation">.</span><span class="token function">terms</span><span class="token punctuation">(</span><span class="token string">"cityAgg"</span><span class="token punctuation">)</span>
                                 <span class="token punctuation">.</span><span class="token function">field</span><span class="token punctuation">(</span><span class="token string">"city"</span><span class="token punctuation">)</span>
                                 <span class="token punctuation">.</span><span class="token function">size</span><span class="token punctuation">(</span><span class="token number">100</span><span class="token punctuation">)</span>
                                <span class="token punctuation">)</span><span class="token punctuation">;</span>
    request<span class="token punctuation">.</span><span class="token function">source</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">aggregation</span><span class="token punctuation">(</span><span class="token class-name">AggregationBuilders</span>
                                 <span class="token punctuation">.</span><span class="token function">terms</span><span class="token punctuation">(</span><span class="token string">"starAgg"</span><span class="token punctuation">)</span>
                                 <span class="token punctuation">.</span><span class="token function">field</span><span class="token punctuation">(</span><span class="token string">"starName"</span><span class="token punctuation">)</span>
                                 <span class="token punctuation">.</span><span class="token function">size</span><span class="token punctuation">(</span><span class="token number">100</span><span class="token punctuation">)</span>
                                <span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span>

<span class="token keyword">private</span> <span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">String</span><span class="token punctuation">></span></span> <span class="token function">getAggByName</span><span class="token punctuation">(</span><span class="token class-name">Aggregations</span> aggregations<span class="token punctuation">,</span> <span class="token class-name">String</span> aggName<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    <span class="token comment">// 4.1.根据聚合名称获取聚合结果</span>
    <span class="token class-name">Terms</span> brandTerms <span class="token operator">=</span> aggregations<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>aggName<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// 4.2.获取buckets</span>
    <span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token operator">?</span> <span class="token keyword">extends</span> <span class="token class-name">Terms<span class="token punctuation">.</span>Bucket</span><span class="token punctuation">></span></span> buckets <span class="token operator">=</span> brandTerms<span class="token punctuation">.</span><span class="token function">getBuckets</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// 4.3.遍历</span>
    <span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">String</span><span class="token punctuation">></span></span> brandList <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ArrayList</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token class-name">Terms<span class="token punctuation">.</span>Bucket</span> bucket <span class="token operator">:</span> buckets<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token comment">// 4.4.获取key</span>
        <span class="token class-name">String</span> key <span class="token operator">=</span> bucket<span class="token punctuation">.</span><span class="token function">getKeyAsString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        brandList<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>key<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>
    <span class="token keyword">return</span> brandList<span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>







<h1 id="2-自动补全"><a href="#2-自动补全" class="headerlink" title="2.自动补全"></a>2.自动补全</h1><p>当用户在搜索框输入字符时，我们应该提示出与该字符有关的搜索项，如图：</p>
<p><img src="assets/image-20210723204936367.png" alt="image-20210723204936367"></p>
<p>这种根据用户输入的字母，提示完整词条的功能，就是自动补全了。</p>
<p>因为需要根据拼音字母来推断，因此要用到拼音分词功能。</p>
<h2 id="2-1-拼音分词器"><a href="#2-1-拼音分词器" class="headerlink" title="2.1.拼音分词器"></a>2.1.拼音分词器</h2><p>要实现根据字母做补全，就必须对文档按照拼音分词。在GitHub上恰好有elasticsearch的拼音分词插件。地址：<a target="_blank" rel="noopener" href="https://github.com/medcl/elasticsearch-analysis-pinyin">https://github.com/medcl/elasticsearch-analysis-pinyin</a></p>
<p><img src="assets/image-20210723205932746.png" alt="image-20210723205932746"></p>
<p>课前资料中也提供了拼音分词器的安装包：</p>
<p><img src="assets/image-20210723205722303.png" alt="image-20210723205722303"> </p>
<p>安装方式与IK分词器一样，分三步：</p>
<p>​    ①解压</p>
<p>​    ②上传到虚拟机中，elasticsearch的plugin目录</p>
<p>​    ③重启elasticsearch</p>
<p>​    ④测试</p>
<p>详细安装步骤可以参考IK分词器的安装过程。</p>
<p>测试用法如下：</p>
<pre class="line-numbers language-json" data-language="json"><code class="language-json">POST /_analyze
<span class="token punctuation">&#123;</span>
  <span class="token property">"text"</span><span class="token operator">:</span> <span class="token string">"如家酒店还不错"</span><span class="token punctuation">,</span>
  <span class="token property">"analyzer"</span><span class="token operator">:</span> <span class="token string">"pinyin"</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<p>结果：</p>
<p><img src="assets/image-20210723210126506.png" alt="image-20210723210126506"> </p>
<h2 id="2-2-自定义分词器"><a href="#2-2-自定义分词器" class="headerlink" title="2.2.自定义分词器"></a>2.2.自定义分词器</h2><p>默认的拼音分词器会将每个汉字单独分为拼音，而我们希望的是每个词条形成一组拼音，需要对拼音分词器做个性化定制，形成自定义分词器。</p>
<p>elasticsearch中分词器（analyzer）的组成包含三部分：</p>
<ul>
<li>character filters：在tokenizer之前对文本进行处理。例如删除字符、替换字符</li>
<li>tokenizer：将文本按照一定的规则切割成词条（term）。例如keyword，就是不分词；还有ik_smart</li>
<li>tokenizer filter：将tokenizer输出的词条做进一步处理。例如大小写转换、同义词处理、拼音处理等</li>
</ul>
<p>文档分词时会依次由这三部分来处理文档：</p>
<p>   <img src="assets/image-20210723210427878.png" alt="image-20210723210427878"></p>
<p>声明自定义分词器的语法如下：</p>
<pre class="line-numbers language-json" data-language="json"><code class="language-json">PUT /test
<span class="token punctuation">&#123;</span>
  <span class="token property">"settings"</span><span class="token operator">:</span> <span class="token punctuation">&#123;</span>
    <span class="token property">"analysis"</span><span class="token operator">:</span> <span class="token punctuation">&#123;</span>
      <span class="token property">"analyzer"</span><span class="token operator">:</span> <span class="token punctuation">&#123;</span> <span class="token comment">// 自定义分词器</span>
        <span class="token property">"my_analyzer"</span><span class="token operator">:</span> <span class="token punctuation">&#123;</span>  <span class="token comment">// 分词器名称</span>
          <span class="token property">"tokenizer"</span><span class="token operator">:</span> <span class="token string">"ik_max_word"</span><span class="token punctuation">,</span>
          <span class="token property">"filter"</span><span class="token operator">:</span> <span class="token string">"py"</span>
        <span class="token punctuation">&#125;</span>
      <span class="token punctuation">&#125;</span><span class="token punctuation">,</span>
      <span class="token property">"filter"</span><span class="token operator">:</span> <span class="token punctuation">&#123;</span> <span class="token comment">// 自定义tokenizer filter</span>
        <span class="token property">"py"</span><span class="token operator">:</span> <span class="token punctuation">&#123;</span> <span class="token comment">// 过滤器名称</span>
          <span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"pinyin"</span><span class="token punctuation">,</span> <span class="token comment">// 过滤器类型，这里是pinyin</span>
		  <span class="token property">"keep_full_pinyin"</span><span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
          <span class="token property">"keep_joined_full_pinyin"</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
          <span class="token property">"keep_original"</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
          <span class="token property">"limit_first_letter_length"</span><span class="token operator">:</span> <span class="token number">16</span><span class="token punctuation">,</span>
          <span class="token property">"remove_duplicated_term"</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
          <span class="token property">"none_chinese_pinyin_tokenize"</span><span class="token operator">:</span> <span class="token boolean">false</span>
        <span class="token punctuation">&#125;</span>
      <span class="token punctuation">&#125;</span>
    <span class="token punctuation">&#125;</span>
  <span class="token punctuation">&#125;</span><span class="token punctuation">,</span>
  <span class="token property">"mappings"</span><span class="token operator">:</span> <span class="token punctuation">&#123;</span>
    <span class="token property">"properties"</span><span class="token operator">:</span> <span class="token punctuation">&#123;</span>
      <span class="token property">"name"</span><span class="token operator">:</span> <span class="token punctuation">&#123;</span>
        <span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"text"</span><span class="token punctuation">,</span>
        <span class="token property">"analyzer"</span><span class="token operator">:</span> <span class="token string">"my_analyzer"</span><span class="token punctuation">,</span>
        <span class="token property">"search_analyzer"</span><span class="token operator">:</span> <span class="token string">"ik_smart"</span>
      <span class="token punctuation">&#125;</span>
    <span class="token punctuation">&#125;</span>
  <span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>



<p>测试：</p>
<p><img src="assets/image-20210723211829150.png" alt="image-20210723211829150"></p>
<p>总结：</p>
<p>如何使用拼音分词器？</p>
<ul>
<li><p>①下载pinyin分词器</p>
</li>
<li><p>②解压并放到elasticsearch的plugin目录</p>
</li>
<li><p>③重启即可</p>
</li>
</ul>
<p>如何自定义分词器？</p>
<ul>
<li><p>①创建索引库时，在settings中配置，可以包含三部分</p>
</li>
<li><p>②character filter</p>
</li>
<li><p>③tokenizer</p>
</li>
<li><p>④filter</p>
</li>
</ul>
<p>拼音分词器注意事项？</p>
<ul>
<li>为了避免搜索到同音字，搜索时不要使用拼音分词器</li>
</ul>
<h2 id="2-3-自动补全查询"><a href="#2-3-自动补全查询" class="headerlink" title="2.3.自动补全查询"></a>2.3.自动补全查询</h2><p>elasticsearch提供了<a target="_blank" rel="noopener" href="https://www.elastic.co/guide/en/elasticsearch/reference/7.6/search-suggesters.html">Completion Suggester</a>查询来实现自动补全功能。这个查询会匹配以用户输入内容开头的词条并返回。为了提高补全查询的效率，对于文档中字段的类型有一些约束：</p>
<ul>
<li><p>参与补全查询的字段必须是completion类型。</p>
</li>
<li><p>字段的内容一般是用来补全的多个词条形成的数组。</p>
</li>
</ul>
<p>比如，一个这样的索引库：</p>
<pre class="line-numbers language-json" data-language="json"><code class="language-json"><span class="token comment">// 创建索引库</span>
PUT test
<span class="token punctuation">&#123;</span>
  <span class="token property">"mappings"</span><span class="token operator">:</span> <span class="token punctuation">&#123;</span>
    <span class="token property">"properties"</span><span class="token operator">:</span> <span class="token punctuation">&#123;</span>
      <span class="token property">"title"</span><span class="token operator">:</span><span class="token punctuation">&#123;</span>
        <span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"completion"</span>
      <span class="token punctuation">&#125;</span>
    <span class="token punctuation">&#125;</span>
  <span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<p>然后插入下面的数据：</p>
<pre class="line-numbers language-json" data-language="json"><code class="language-json"><span class="token comment">// 示例数据</span>
POST test/_doc
<span class="token punctuation">&#123;</span>
  <span class="token property">"title"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">"Sony"</span><span class="token punctuation">,</span> <span class="token string">"WH-1000XM3"</span><span class="token punctuation">]</span>
<span class="token punctuation">&#125;</span>
POST test/_doc
<span class="token punctuation">&#123;</span>
  <span class="token property">"title"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">"SK-II"</span><span class="token punctuation">,</span> <span class="token string">"PITERA"</span><span class="token punctuation">]</span>
<span class="token punctuation">&#125;</span>
POST test/_doc
<span class="token punctuation">&#123;</span>
  <span class="token property">"title"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">"Nintendo"</span><span class="token punctuation">,</span> <span class="token string">"switch"</span><span class="token punctuation">]</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>



<p>查询的DSL语句如下：</p>
<pre class="line-numbers language-json" data-language="json"><code class="language-json"><span class="token comment">// 自动补全查询</span>
GET /test/_search
<span class="token punctuation">&#123;</span>
  <span class="token property">"suggest"</span><span class="token operator">:</span> <span class="token punctuation">&#123;</span>
    <span class="token property">"title_suggest"</span><span class="token operator">:</span> <span class="token punctuation">&#123;</span>
      <span class="token property">"text"</span><span class="token operator">:</span> <span class="token string">"s"</span><span class="token punctuation">,</span> <span class="token comment">// 关键字</span>
      <span class="token property">"completion"</span><span class="token operator">:</span> <span class="token punctuation">&#123;</span>
        <span class="token property">"field"</span><span class="token operator">:</span> <span class="token string">"title"</span><span class="token punctuation">,</span> <span class="token comment">// 补全查询的字段</span>
        <span class="token property">"skip_duplicates"</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> <span class="token comment">// 跳过重复的</span>
        <span class="token property">"size"</span><span class="token operator">:</span> <span class="token number">10</span> <span class="token comment">// 获取前10条结果</span>
      <span class="token punctuation">&#125;</span>
    <span class="token punctuation">&#125;</span>
  <span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>







<h2 id="2-4-实现酒店搜索框自动补全"><a href="#2-4-实现酒店搜索框自动补全" class="headerlink" title="2.4.实现酒店搜索框自动补全"></a>2.4.实现酒店搜索框自动补全</h2><p>现在，我们的hotel索引库还没有设置拼音分词器，需要修改索引库中的配置。但是我们知道索引库是无法修改的，只能删除然后重新创建。</p>
<p>另外，我们需要添加一个字段，用来做自动补全，将brand、suggestion、city等都放进去，作为自动补全的提示。</p>
<p>因此，总结一下，我们需要做的事情包括：</p>
<ol>
<li><p>修改hotel索引库结构，设置自定义拼音分词器</p>
</li>
<li><p>修改索引库的name、all字段，使用自定义分词器</p>
</li>
<li><p>索引库添加一个新字段suggestion，类型为completion类型，使用自定义的分词器</p>
</li>
<li><p>给HotelDoc类添加suggestion字段，内容包含brand、business</p>
</li>
<li><p>重新导入数据到hotel库</p>
</li>
</ol>
<h3 id="2-4-1-修改酒店映射结构"><a href="#2-4-1-修改酒店映射结构" class="headerlink" title="2.4.1.修改酒店映射结构"></a>2.4.1.修改酒店映射结构</h3><p>代码如下：</p>
<pre class="line-numbers language-json" data-language="json"><code class="language-json"><span class="token comment">// 酒店数据索引库</span>
PUT /hotel
<span class="token punctuation">&#123;</span>
  <span class="token property">"settings"</span><span class="token operator">:</span> <span class="token punctuation">&#123;</span>
    <span class="token property">"analysis"</span><span class="token operator">:</span> <span class="token punctuation">&#123;</span>
      <span class="token property">"analyzer"</span><span class="token operator">:</span> <span class="token punctuation">&#123;</span>
        <span class="token property">"text_anlyzer"</span><span class="token operator">:</span> <span class="token punctuation">&#123;</span>
          <span class="token property">"tokenizer"</span><span class="token operator">:</span> <span class="token string">"ik_max_word"</span><span class="token punctuation">,</span>
          <span class="token property">"filter"</span><span class="token operator">:</span> <span class="token string">"py"</span>
        <span class="token punctuation">&#125;</span><span class="token punctuation">,</span>
        <span class="token property">"completion_analyzer"</span><span class="token operator">:</span> <span class="token punctuation">&#123;</span>
          <span class="token property">"tokenizer"</span><span class="token operator">:</span> <span class="token string">"keyword"</span><span class="token punctuation">,</span>
          <span class="token property">"filter"</span><span class="token operator">:</span> <span class="token string">"py"</span>
        <span class="token punctuation">&#125;</span>
      <span class="token punctuation">&#125;</span><span class="token punctuation">,</span>
      <span class="token property">"filter"</span><span class="token operator">:</span> <span class="token punctuation">&#123;</span>
        <span class="token property">"py"</span><span class="token operator">:</span> <span class="token punctuation">&#123;</span>
          <span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"pinyin"</span><span class="token punctuation">,</span>
          <span class="token property">"keep_full_pinyin"</span><span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
          <span class="token property">"keep_joined_full_pinyin"</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
          <span class="token property">"keep_original"</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
          <span class="token property">"limit_first_letter_length"</span><span class="token operator">:</span> <span class="token number">16</span><span class="token punctuation">,</span>
          <span class="token property">"remove_duplicated_term"</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
          <span class="token property">"none_chinese_pinyin_tokenize"</span><span class="token operator">:</span> <span class="token boolean">false</span>
        <span class="token punctuation">&#125;</span>
      <span class="token punctuation">&#125;</span>
    <span class="token punctuation">&#125;</span>
  <span class="token punctuation">&#125;</span><span class="token punctuation">,</span>
  <span class="token property">"mappings"</span><span class="token operator">:</span> <span class="token punctuation">&#123;</span>
    <span class="token property">"properties"</span><span class="token operator">:</span> <span class="token punctuation">&#123;</span>
      <span class="token property">"id"</span><span class="token operator">:</span><span class="token punctuation">&#123;</span>
        <span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"keyword"</span>
      <span class="token punctuation">&#125;</span><span class="token punctuation">,</span>
      <span class="token property">"name"</span><span class="token operator">:</span><span class="token punctuation">&#123;</span>
        <span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"text"</span><span class="token punctuation">,</span>
        <span class="token property">"analyzer"</span><span class="token operator">:</span> <span class="token string">"text_anlyzer"</span><span class="token punctuation">,</span>
        <span class="token property">"search_analyzer"</span><span class="token operator">:</span> <span class="token string">"ik_smart"</span><span class="token punctuation">,</span>
        <span class="token property">"copy_to"</span><span class="token operator">:</span> <span class="token string">"all"</span>
      <span class="token punctuation">&#125;</span><span class="token punctuation">,</span>
      <span class="token property">"address"</span><span class="token operator">:</span><span class="token punctuation">&#123;</span>
        <span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"keyword"</span><span class="token punctuation">,</span>
        <span class="token property">"index"</span><span class="token operator">:</span> <span class="token boolean">false</span>
      <span class="token punctuation">&#125;</span><span class="token punctuation">,</span>
      <span class="token property">"price"</span><span class="token operator">:</span><span class="token punctuation">&#123;</span>
        <span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"integer"</span>
      <span class="token punctuation">&#125;</span><span class="token punctuation">,</span>
      <span class="token property">"score"</span><span class="token operator">:</span><span class="token punctuation">&#123;</span>
        <span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"integer"</span>
      <span class="token punctuation">&#125;</span><span class="token punctuation">,</span>
      <span class="token property">"brand"</span><span class="token operator">:</span><span class="token punctuation">&#123;</span>
        <span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"keyword"</span><span class="token punctuation">,</span>
        <span class="token property">"copy_to"</span><span class="token operator">:</span> <span class="token string">"all"</span>
      <span class="token punctuation">&#125;</span><span class="token punctuation">,</span>
      <span class="token property">"city"</span><span class="token operator">:</span><span class="token punctuation">&#123;</span>
        <span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"keyword"</span>
      <span class="token punctuation">&#125;</span><span class="token punctuation">,</span>
      <span class="token property">"starName"</span><span class="token operator">:</span><span class="token punctuation">&#123;</span>
        <span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"keyword"</span>
      <span class="token punctuation">&#125;</span><span class="token punctuation">,</span>
      <span class="token property">"business"</span><span class="token operator">:</span><span class="token punctuation">&#123;</span>
        <span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"keyword"</span><span class="token punctuation">,</span>
        <span class="token property">"copy_to"</span><span class="token operator">:</span> <span class="token string">"all"</span>
      <span class="token punctuation">&#125;</span><span class="token punctuation">,</span>
      <span class="token property">"location"</span><span class="token operator">:</span><span class="token punctuation">&#123;</span>
        <span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"geo_point"</span>
      <span class="token punctuation">&#125;</span><span class="token punctuation">,</span>
      <span class="token property">"pic"</span><span class="token operator">:</span><span class="token punctuation">&#123;</span>
        <span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"keyword"</span><span class="token punctuation">,</span>
        <span class="token property">"index"</span><span class="token operator">:</span> <span class="token boolean">false</span>
      <span class="token punctuation">&#125;</span><span class="token punctuation">,</span>
      <span class="token property">"all"</span><span class="token operator">:</span><span class="token punctuation">&#123;</span>
        <span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"text"</span><span class="token punctuation">,</span>
        <span class="token property">"analyzer"</span><span class="token operator">:</span> <span class="token string">"text_anlyzer"</span><span class="token punctuation">,</span>
        <span class="token property">"search_analyzer"</span><span class="token operator">:</span> <span class="token string">"ik_smart"</span>
      <span class="token punctuation">&#125;</span><span class="token punctuation">,</span>
      <span class="token property">"suggestion"</span><span class="token operator">:</span><span class="token punctuation">&#123;</span>
          <span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"completion"</span><span class="token punctuation">,</span>
          <span class="token property">"analyzer"</span><span class="token operator">:</span> <span class="token string">"completion_analyzer"</span>
      <span class="token punctuation">&#125;</span>
    <span class="token punctuation">&#125;</span>
  <span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>



<h3 id="2-4-2-修改HotelDoc实体"><a href="#2-4-2-修改HotelDoc实体" class="headerlink" title="2.4.2.修改HotelDoc实体"></a>2.4.2.修改HotelDoc实体</h3><p>HotelDoc中要添加一个字段，用来做自动补全，内容可以是酒店品牌、城市、商圈等信息。按照自动补全字段的要求，最好是这些字段的数组。</p>
<p>因此我们在HotelDoc中添加一个suggestion字段，类型为<code>List&lt;String&gt;</code>，然后将brand、city、business等信息放到里面。</p>
<p>代码如下：</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">package</span> <span class="token namespace">cn<span class="token punctuation">.</span>itcast<span class="token punctuation">.</span>hotel<span class="token punctuation">.</span>pojo</span><span class="token punctuation">;</span>

<span class="token keyword">import</span> <span class="token import"><span class="token namespace">lombok<span class="token punctuation">.</span></span><span class="token class-name">Data</span></span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token import"><span class="token namespace">lombok<span class="token punctuation">.</span></span><span class="token class-name">NoArgsConstructor</span></span><span class="token punctuation">;</span>

<span class="token keyword">import</span> <span class="token import"><span class="token namespace">java<span class="token punctuation">.</span>util<span class="token punctuation">.</span></span><span class="token class-name">ArrayList</span></span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token import"><span class="token namespace">java<span class="token punctuation">.</span>util<span class="token punctuation">.</span></span><span class="token class-name">Arrays</span></span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token import"><span class="token namespace">java<span class="token punctuation">.</span>util<span class="token punctuation">.</span></span><span class="token class-name">Collections</span></span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token import"><span class="token namespace">java<span class="token punctuation">.</span>util<span class="token punctuation">.</span></span><span class="token class-name">List</span></span><span class="token punctuation">;</span>

<span class="token annotation punctuation">@Data</span>
<span class="token annotation punctuation">@NoArgsConstructor</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">HotelDoc</span> <span class="token punctuation">&#123;</span>
    <span class="token keyword">private</span> <span class="token class-name">Long</span> id<span class="token punctuation">;</span>
    <span class="token keyword">private</span> <span class="token class-name">String</span> name<span class="token punctuation">;</span>
    <span class="token keyword">private</span> <span class="token class-name">String</span> address<span class="token punctuation">;</span>
    <span class="token keyword">private</span> <span class="token class-name">Integer</span> price<span class="token punctuation">;</span>
    <span class="token keyword">private</span> <span class="token class-name">Integer</span> score<span class="token punctuation">;</span>
    <span class="token keyword">private</span> <span class="token class-name">String</span> brand<span class="token punctuation">;</span>
    <span class="token keyword">private</span> <span class="token class-name">String</span> city<span class="token punctuation">;</span>
    <span class="token keyword">private</span> <span class="token class-name">String</span> starName<span class="token punctuation">;</span>
    <span class="token keyword">private</span> <span class="token class-name">String</span> business<span class="token punctuation">;</span>
    <span class="token keyword">private</span> <span class="token class-name">String</span> location<span class="token punctuation">;</span>
    <span class="token keyword">private</span> <span class="token class-name">String</span> pic<span class="token punctuation">;</span>
    <span class="token keyword">private</span> <span class="token class-name">Object</span> distance<span class="token punctuation">;</span>
    <span class="token keyword">private</span> <span class="token class-name">Boolean</span> isAD<span class="token punctuation">;</span>
    <span class="token keyword">private</span> <span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">String</span><span class="token punctuation">></span></span> suggestion<span class="token punctuation">;</span>

    <span class="token keyword">public</span> <span class="token class-name">HotelDoc</span><span class="token punctuation">(</span><span class="token class-name">Hotel</span> hotel<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span>id <span class="token operator">=</span> hotel<span class="token punctuation">.</span><span class="token function">getId</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span>name <span class="token operator">=</span> hotel<span class="token punctuation">.</span><span class="token function">getName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span>address <span class="token operator">=</span> hotel<span class="token punctuation">.</span><span class="token function">getAddress</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span>price <span class="token operator">=</span> hotel<span class="token punctuation">.</span><span class="token function">getPrice</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span>score <span class="token operator">=</span> hotel<span class="token punctuation">.</span><span class="token function">getScore</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span>brand <span class="token operator">=</span> hotel<span class="token punctuation">.</span><span class="token function">getBrand</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span>city <span class="token operator">=</span> hotel<span class="token punctuation">.</span><span class="token function">getCity</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span>starName <span class="token operator">=</span> hotel<span class="token punctuation">.</span><span class="token function">getStarName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span>business <span class="token operator">=</span> hotel<span class="token punctuation">.</span><span class="token function">getBusiness</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span>location <span class="token operator">=</span> hotel<span class="token punctuation">.</span><span class="token function">getLatitude</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">", "</span> <span class="token operator">+</span> hotel<span class="token punctuation">.</span><span class="token function">getLongitude</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span>pic <span class="token operator">=</span> hotel<span class="token punctuation">.</span><span class="token function">getPic</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 组装suggestion</span>
        <span class="token keyword">if</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>business<span class="token punctuation">.</span><span class="token function">contains</span><span class="token punctuation">(</span><span class="token string">"/"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>
            <span class="token comment">// business有多个值，需要切割</span>
            <span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> arr <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>business<span class="token punctuation">.</span><span class="token function">split</span><span class="token punctuation">(</span><span class="token string">"/"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token comment">// 添加元素</span>
            <span class="token keyword">this</span><span class="token punctuation">.</span>suggestion <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ArrayList</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token keyword">this</span><span class="token punctuation">.</span>suggestion<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>brand<span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token class-name">Collections</span><span class="token punctuation">.</span><span class="token function">addAll</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>suggestion<span class="token punctuation">,</span> arr<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">&#125;</span><span class="token keyword">else</span> <span class="token punctuation">&#123;</span>
            <span class="token keyword">this</span><span class="token punctuation">.</span>suggestion <span class="token operator">=</span> <span class="token class-name">Arrays</span><span class="token punctuation">.</span><span class="token function">asList</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>brand<span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">.</span>business<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">&#125;</span>
    <span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>



<h3 id="2-4-3-重新导入"><a href="#2-4-3-重新导入" class="headerlink" title="2.4.3.重新导入"></a>2.4.3.重新导入</h3><p>重新执行之前编写的导入数据功能，可以看到新的酒店数据中包含了suggestion：</p>
<p><img src="assets/image-20210723213546183.png" alt="image-20210723213546183"></p>
<h3 id="2-4-4-自动补全查询的JavaAPI"><a href="#2-4-4-自动补全查询的JavaAPI" class="headerlink" title="2.4.4.自动补全查询的JavaAPI"></a>2.4.4.自动补全查询的JavaAPI</h3><p>之前我们学习了自动补全查询的DSL，而没有学习对应的JavaAPI，这里给出一个示例：</p>
<p><img src="assets/image-20210723213759922.png" alt="image-20210723213759922"></p>
<p>而自动补全的结果也比较特殊，解析的代码如下：</p>
<p><img src="assets/image-20210723213917524.png" alt="image-20210723213917524"></p>
<h3 id="2-4-5-实现搜索框自动补全"><a href="#2-4-5-实现搜索框自动补全" class="headerlink" title="2.4.5.实现搜索框自动补全"></a>2.4.5.实现搜索框自动补全</h3><p>查看前端页面，可以发现当我们在输入框键入时，前端会发起ajax请求：</p>
<p><img src="assets/image-20210723214021062.png" alt="image-20210723214021062"></p>
<p>返回值是补全词条的集合，类型为<code>List&lt;String&gt;</code></p>
<p>1）在<code>cn.itcast.hotel.web</code>包下的<code>HotelController</code>中添加新接口，接收新的请求：</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token annotation punctuation">@GetMapping</span><span class="token punctuation">(</span><span class="token string">"suggestion"</span><span class="token punctuation">)</span>
<span class="token keyword">public</span> <span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">String</span><span class="token punctuation">></span></span> <span class="token function">getSuggestions</span><span class="token punctuation">(</span><span class="token annotation punctuation">@RequestParam</span><span class="token punctuation">(</span><span class="token string">"key"</span><span class="token punctuation">)</span> <span class="token class-name">String</span> prefix<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    <span class="token keyword">return</span> hotelService<span class="token punctuation">.</span><span class="token function">getSuggestions</span><span class="token punctuation">(</span>prefix<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre>



<p>2）在<code>cn.itcast.hotel.service</code>包下的<code>IhotelService</code>中添加方法：</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">String</span><span class="token punctuation">></span></span> <span class="token function">getSuggestions</span><span class="token punctuation">(</span><span class="token class-name">String</span> prefix<span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre>



<p>3）在<code>cn.itcast.hotel.service.impl.HotelService</code>中实现该方法：</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token annotation punctuation">@Override</span>
<span class="token keyword">public</span> <span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">String</span><span class="token punctuation">></span></span> <span class="token function">getSuggestions</span><span class="token punctuation">(</span><span class="token class-name">String</span> prefix<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    <span class="token keyword">try</span> <span class="token punctuation">&#123;</span>
        <span class="token comment">// 1.准备Request</span>
        <span class="token class-name">SearchRequest</span> request <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">SearchRequest</span><span class="token punctuation">(</span><span class="token string">"hotel"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 2.准备DSL</span>
        request<span class="token punctuation">.</span><span class="token function">source</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">suggest</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">SuggestBuilder</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">addSuggestion</span><span class="token punctuation">(</span>
            <span class="token string">"suggestions"</span><span class="token punctuation">,</span>
            <span class="token class-name">SuggestBuilders</span><span class="token punctuation">.</span><span class="token function">completionSuggestion</span><span class="token punctuation">(</span><span class="token string">"suggestion"</span><span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">prefix</span><span class="token punctuation">(</span>prefix<span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">skipDuplicates</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">size</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">)</span>
        <span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 3.发起请求</span>
        <span class="token class-name">SearchResponse</span> response <span class="token operator">=</span> client<span class="token punctuation">.</span><span class="token function">search</span><span class="token punctuation">(</span>request<span class="token punctuation">,</span> <span class="token class-name">RequestOptions</span><span class="token punctuation">.</span><span class="token constant">DEFAULT</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 4.解析结果</span>
        <span class="token class-name">Suggest</span> suggest <span class="token operator">=</span> response<span class="token punctuation">.</span><span class="token function">getSuggest</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 4.1.根据补全查询名称，获取补全结果</span>
        <span class="token class-name">CompletionSuggestion</span> suggestions <span class="token operator">=</span> suggest<span class="token punctuation">.</span><span class="token function">getSuggestion</span><span class="token punctuation">(</span><span class="token string">"suggestions"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 4.2.获取options</span>
        <span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">CompletionSuggestion<span class="token punctuation">.</span>Entry<span class="token punctuation">.</span>Option</span><span class="token punctuation">></span></span> options <span class="token operator">=</span> suggestions<span class="token punctuation">.</span><span class="token function">getOptions</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 4.3.遍历</span>
        <span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">String</span><span class="token punctuation">></span></span> list <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ArrayList</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token punctuation">></span></span><span class="token punctuation">(</span>options<span class="token punctuation">.</span><span class="token function">size</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token class-name">CompletionSuggestion<span class="token punctuation">.</span>Entry<span class="token punctuation">.</span>Option</span> option <span class="token operator">:</span> options<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
            <span class="token class-name">String</span> text <span class="token operator">=</span> option<span class="token punctuation">.</span><span class="token function">getText</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            list<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>text<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">&#125;</span>
        <span class="token keyword">return</span> list<span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">IOException</span> e<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">RuntimeException</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>





<h1 id="3-数据同步"><a href="#3-数据同步" class="headerlink" title="3.数据同步"></a>3.数据同步</h1><p>elasticsearch中的酒店数据来自于mysql数据库，因此mysql数据发生改变时，elasticsearch也必须跟着改变，这个就是elasticsearch与mysql之间的<strong>数据同步</strong>。</p>
<p><img src="assets/image-20210723214758392.png" alt="image-20210723214758392"></p>
<h2 id="3-1-思路分析"><a href="#3-1-思路分析" class="headerlink" title="3.1.思路分析"></a>3.1.思路分析</h2><p>常见的数据同步方案有三种：</p>
<ul>
<li>同步调用</li>
<li>异步通知</li>
<li>监听binlog</li>
</ul>
<h3 id="3-1-1-同步调用"><a href="#3-1-1-同步调用" class="headerlink" title="3.1.1.同步调用"></a>3.1.1.同步调用</h3><p>方案一：同步调用</p>
<p><img src="assets/image-20210723214931869.png" alt="image-20210723214931869"></p>
<p>基本步骤如下：</p>
<ul>
<li>hotel-demo对外提供接口，用来修改elasticsearch中的数据</li>
<li>酒店管理服务在完成数据库操作后，直接调用hotel-demo提供的接口，</li>
</ul>
<h3 id="3-1-2-异步通知"><a href="#3-1-2-异步通知" class="headerlink" title="3.1.2.异步通知"></a>3.1.2.异步通知</h3><p>方案二：异步通知</p>
<p><img src="assets/image-20210723215140735.png" alt="image-20210723215140735"></p>
<p>流程如下：</p>
<ul>
<li>hotel-admin对mysql数据库数据完成增、删、改后，发送MQ消息</li>
<li>hotel-demo监听MQ，接收到消息后完成elasticsearch数据修改</li>
</ul>
<h3 id="3-1-3-监听binlog"><a href="#3-1-3-监听binlog" class="headerlink" title="3.1.3.监听binlog"></a>3.1.3.监听binlog</h3><p>方案三：监听binlog</p>
<p><img src="assets/image-20210723215518541.png" alt="image-20210723215518541"></p>
<p>流程如下：</p>
<ul>
<li>给mysql开启binlog功能</li>
<li>mysql完成增、删、改操作都会记录在binlog中</li>
<li>hotel-demo基于canal监听binlog变化，实时更新elasticsearch中的内容</li>
</ul>
<h3 id="3-1-4-选择"><a href="#3-1-4-选择" class="headerlink" title="3.1.4.选择"></a>3.1.4.选择</h3><p>方式一：同步调用</p>
<ul>
<li>优点：实现简单，粗暴</li>
<li>缺点：业务耦合度高</li>
</ul>
<p>方式二：异步通知</p>
<ul>
<li>优点：低耦合，实现难度一般</li>
<li>缺点：依赖mq的可靠性</li>
</ul>
<p>方式三：监听binlog</p>
<ul>
<li>优点：完全解除服务间耦合</li>
<li>缺点：开启binlog增加数据库负担、实现复杂度高</li>
</ul>
<h2 id="3-2-实现数据同步"><a href="#3-2-实现数据同步" class="headerlink" title="3.2.实现数据同步"></a>3.2.实现数据同步</h2><h3 id="3-2-1-思路"><a href="#3-2-1-思路" class="headerlink" title="3.2.1.思路"></a>3.2.1.思路</h3><p>利用课前资料提供的hotel-admin项目作为酒店管理的微服务。当酒店数据发生增、删、改时，要求对elasticsearch中数据也要完成相同操作。</p>
<p>步骤：</p>
<ul>
<li><p>导入课前资料提供的hotel-admin项目，启动并测试酒店数据的CRUD</p>
</li>
<li><p>声明exchange、queue、RoutingKey</p>
</li>
<li><p>在hotel-admin中的增、删、改业务中完成消息发送</p>
</li>
<li><p>在hotel-demo中完成消息监听，并更新elasticsearch中数据</p>
</li>
<li><p>启动并测试数据同步功能</p>
</li>
</ul>
<h3 id="3-2-2-导入demo"><a href="#3-2-2-导入demo" class="headerlink" title="3.2.2.导入demo"></a>3.2.2.导入demo</h3><p>导入课前资料提供的hotel-admin项目：</p>
<p><img src="assets/image-20210723220237930.png" alt="image-20210723220237930"></p>
<p>运行后，访问 <a target="_blank" rel="noopener" href="http://localhost:8099/">http://localhost:8099</a></p>
<p><img src="assets/image-20210723220354464.png" alt="image-20210723220354464"></p>
<p>其中包含了酒店的CRUD功能：</p>
<p><img src="assets/image-20210723220511090.png" alt="image-20210723220511090"></p>
<h3 id="3-2-3-声明交换机、队列"><a href="#3-2-3-声明交换机、队列" class="headerlink" title="3.2.3.声明交换机、队列"></a>3.2.3.声明交换机、队列</h3><p>MQ结构如图：</p>
<p><img src="assets/image-20210723215850307.png" alt="image-20210723215850307"></p>
<h4 id="1）引入依赖"><a href="#1）引入依赖" class="headerlink" title="1）引入依赖"></a>1）引入依赖</h4><p>在hotel-admin、hotel-demo中引入rabbitmq的依赖：</p>
<pre class="line-numbers language-markup" data-language="markup"><code class="language-markup"><span class="token comment">&lt;!--amqp--></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>dependency</span><span class="token punctuation">></span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>groupId</span><span class="token punctuation">></span></span>org.springframework.boot<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>groupId</span><span class="token punctuation">></span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>artifactId</span><span class="token punctuation">></span></span>spring-boot-starter-amqp<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>artifactId</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>dependency</span><span class="token punctuation">></span></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre>



<h4 id="2）声明队列交换机名称"><a href="#2）声明队列交换机名称" class="headerlink" title="2）声明队列交换机名称"></a>2）声明队列交换机名称</h4><p>在hotel-admin和hotel-demo中的<code>cn.itcast.hotel.constatnts</code>包下新建一个类<code>MqConstants</code>：</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">package</span> <span class="token namespace">cn<span class="token punctuation">.</span>itcast<span class="token punctuation">.</span>hotel<span class="token punctuation">.</span>constatnts</span><span class="token punctuation">;</span>

    <span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">MqConstants</span> <span class="token punctuation">&#123;</span>
    <span class="token comment">/**
     * 交换机
     */</span>
    <span class="token keyword">public</span> <span class="token keyword">final</span> <span class="token keyword">static</span> <span class="token class-name">String</span> <span class="token constant">HOTEL_EXCHANGE</span> <span class="token operator">=</span> <span class="token string">"hotel.topic"</span><span class="token punctuation">;</span>
    <span class="token comment">/**
     * 监听新增和修改的队列
     */</span>
    <span class="token keyword">public</span> <span class="token keyword">final</span> <span class="token keyword">static</span> <span class="token class-name">String</span> <span class="token constant">HOTEL_INSERT_QUEUE</span> <span class="token operator">=</span> <span class="token string">"hotel.insert.queue"</span><span class="token punctuation">;</span>
    <span class="token comment">/**
     * 监听删除的队列
     */</span>
    <span class="token keyword">public</span> <span class="token keyword">final</span> <span class="token keyword">static</span> <span class="token class-name">String</span> <span class="token constant">HOTEL_DELETE_QUEUE</span> <span class="token operator">=</span> <span class="token string">"hotel.delete.queue"</span><span class="token punctuation">;</span>
    <span class="token comment">/**
     * 新增或修改的RoutingKey
     */</span>
    <span class="token keyword">public</span> <span class="token keyword">final</span> <span class="token keyword">static</span> <span class="token class-name">String</span> <span class="token constant">HOTEL_INSERT_KEY</span> <span class="token operator">=</span> <span class="token string">"hotel.insert"</span><span class="token punctuation">;</span>
    <span class="token comment">/**
     * 删除的RoutingKey
     */</span>
    <span class="token keyword">public</span> <span class="token keyword">final</span> <span class="token keyword">static</span> <span class="token class-name">String</span> <span class="token constant">HOTEL_DELETE_KEY</span> <span class="token operator">=</span> <span class="token string">"hotel.delete"</span><span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>



<h4 id="3）声明队列交换机"><a href="#3）声明队列交换机" class="headerlink" title="3）声明队列交换机"></a>3）声明队列交换机</h4><p>在hotel-demo中，定义配置类，声明队列、交换机：</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">package</span> <span class="token namespace">cn<span class="token punctuation">.</span>itcast<span class="token punctuation">.</span>hotel<span class="token punctuation">.</span>config</span><span class="token punctuation">;</span>

<span class="token keyword">import</span> <span class="token import"><span class="token namespace">cn<span class="token punctuation">.</span>itcast<span class="token punctuation">.</span>hotel<span class="token punctuation">.</span>constants<span class="token punctuation">.</span></span><span class="token class-name">MqConstants</span></span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>amqp<span class="token punctuation">.</span>core<span class="token punctuation">.</span></span><span class="token class-name">Binding</span></span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>amqp<span class="token punctuation">.</span>core<span class="token punctuation">.</span></span><span class="token class-name">BindingBuilder</span></span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>amqp<span class="token punctuation">.</span>core<span class="token punctuation">.</span></span><span class="token class-name">Queue</span></span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>amqp<span class="token punctuation">.</span>core<span class="token punctuation">.</span></span><span class="token class-name">TopicExchange</span></span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>context<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span></span><span class="token class-name">Bean</span></span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>context<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span></span><span class="token class-name">Configuration</span></span><span class="token punctuation">;</span>

<span class="token annotation punctuation">@Configuration</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">MqConfig</span> <span class="token punctuation">&#123;</span>
    <span class="token annotation punctuation">@Bean</span>
    <span class="token keyword">public</span> <span class="token class-name">TopicExchange</span> <span class="token function">topicExchange</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>
        <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">TopicExchange</span><span class="token punctuation">(</span><span class="token class-name">MqConstants</span><span class="token punctuation">.</span><span class="token constant">HOTEL_EXCHANGE</span><span class="token punctuation">,</span> <span class="token boolean">true</span><span class="token punctuation">,</span> <span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>

    <span class="token annotation punctuation">@Bean</span>
    <span class="token keyword">public</span> <span class="token class-name">Queue</span> <span class="token function">insertQueue</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>
        <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">Queue</span><span class="token punctuation">(</span><span class="token class-name">MqConstants</span><span class="token punctuation">.</span><span class="token constant">HOTEL_INSERT_QUEUE</span><span class="token punctuation">,</span> <span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>

    <span class="token annotation punctuation">@Bean</span>
    <span class="token keyword">public</span> <span class="token class-name">Queue</span> <span class="token function">deleteQueue</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>
        <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">Queue</span><span class="token punctuation">(</span><span class="token class-name">MqConstants</span><span class="token punctuation">.</span><span class="token constant">HOTEL_DELETE_QUEUE</span><span class="token punctuation">,</span> <span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>

    <span class="token annotation punctuation">@Bean</span>
    <span class="token keyword">public</span> <span class="token class-name">Binding</span> <span class="token function">insertQueueBinding</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>
        <span class="token keyword">return</span> <span class="token class-name">BindingBuilder</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span><span class="token function">insertQueue</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token keyword">to</span><span class="token punctuation">(</span><span class="token function">topicExchange</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token keyword">with</span><span class="token punctuation">(</span><span class="token class-name">MqConstants</span><span class="token punctuation">.</span><span class="token constant">HOTEL_INSERT_KEY</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>

    <span class="token annotation punctuation">@Bean</span>
    <span class="token keyword">public</span> <span class="token class-name">Binding</span> <span class="token function">deleteQueueBinding</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>
        <span class="token keyword">return</span> <span class="token class-name">BindingBuilder</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span><span class="token function">deleteQueue</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token keyword">to</span><span class="token punctuation">(</span><span class="token function">topicExchange</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token keyword">with</span><span class="token punctuation">(</span><span class="token class-name">MqConstants</span><span class="token punctuation">.</span><span class="token constant">HOTEL_DELETE_KEY</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>



<h3 id="3-2-4-发送MQ消息"><a href="#3-2-4-发送MQ消息" class="headerlink" title="3.2.4.发送MQ消息"></a>3.2.4.发送MQ消息</h3><p>在hotel-admin中的增、删、改业务中分别发送MQ消息：</p>
<p><img src="assets/image-20210723221843816.png" alt="image-20210723221843816"></p>
<h3 id="3-2-5-接收MQ消息"><a href="#3-2-5-接收MQ消息" class="headerlink" title="3.2.5.接收MQ消息"></a>3.2.5.接收MQ消息</h3><p>hotel-demo接收到MQ消息要做的事情包括：</p>
<ul>
<li>新增消息：根据传递的hotel的id查询hotel信息，然后新增一条数据到索引库</li>
<li>删除消息：根据传递的hotel的id删除索引库中的一条数据</li>
</ul>
<p>1）首先在hotel-demo的<code>cn.itcast.hotel.service</code>包下的<code>IHotelService</code>中新增新增、删除业务</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">void</span> <span class="token function">deleteById</span><span class="token punctuation">(</span><span class="token class-name">Long</span> id<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">void</span> <span class="token function">insertById</span><span class="token punctuation">(</span><span class="token class-name">Long</span> id<span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre>



<p>2）给hotel-demo中的<code>cn.itcast.hotel.service.impl</code>包下的HotelService中实现业务：</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token annotation punctuation">@Override</span>
<span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">deleteById</span><span class="token punctuation">(</span><span class="token class-name">Long</span> id<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    <span class="token keyword">try</span> <span class="token punctuation">&#123;</span>
        <span class="token comment">// 1.准备Request</span>
        <span class="token class-name">DeleteRequest</span> request <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">DeleteRequest</span><span class="token punctuation">(</span><span class="token string">"hotel"</span><span class="token punctuation">,</span> id<span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 2.发送请求</span>
        client<span class="token punctuation">.</span><span class="token function">delete</span><span class="token punctuation">(</span>request<span class="token punctuation">,</span> <span class="token class-name">RequestOptions</span><span class="token punctuation">.</span><span class="token constant">DEFAULT</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">IOException</span> e<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">RuntimeException</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span>

<span class="token annotation punctuation">@Override</span>
<span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">insertById</span><span class="token punctuation">(</span><span class="token class-name">Long</span> id<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    <span class="token keyword">try</span> <span class="token punctuation">&#123;</span>
        <span class="token comment">// 0.根据id查询酒店数据</span>
        <span class="token class-name">Hotel</span> hotel <span class="token operator">=</span> <span class="token function">getById</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 转换为文档类型</span>
        <span class="token class-name">HotelDoc</span> hotelDoc <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">HotelDoc</span><span class="token punctuation">(</span>hotel<span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token comment">// 1.准备Request对象</span>
        <span class="token class-name">IndexRequest</span> request <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">IndexRequest</span><span class="token punctuation">(</span><span class="token string">"hotel"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">id</span><span class="token punctuation">(</span>hotel<span class="token punctuation">.</span><span class="token function">getId</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 2.准备Json文档</span>
        request<span class="token punctuation">.</span><span class="token function">source</span><span class="token punctuation">(</span><span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">toJSONString</span><span class="token punctuation">(</span>hotelDoc<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token class-name">XContentType</span><span class="token punctuation">.</span><span class="token constant">JSON</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 3.发送请求</span>
        client<span class="token punctuation">.</span><span class="token function">index</span><span class="token punctuation">(</span>request<span class="token punctuation">,</span> <span class="token class-name">RequestOptions</span><span class="token punctuation">.</span><span class="token constant">DEFAULT</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">IOException</span> e<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">RuntimeException</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>



<p>3）编写监听器</p>
<p>在hotel-demo中的<code>cn.itcast.hotel.mq</code>包新增一个类：</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">package</span> <span class="token namespace">cn<span class="token punctuation">.</span>itcast<span class="token punctuation">.</span>hotel<span class="token punctuation">.</span>mq</span><span class="token punctuation">;</span>

<span class="token keyword">import</span> <span class="token import"><span class="token namespace">cn<span class="token punctuation">.</span>itcast<span class="token punctuation">.</span>hotel<span class="token punctuation">.</span>constants<span class="token punctuation">.</span></span><span class="token class-name">MqConstants</span></span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token import"><span class="token namespace">cn<span class="token punctuation">.</span>itcast<span class="token punctuation">.</span>hotel<span class="token punctuation">.</span>service<span class="token punctuation">.</span></span><span class="token class-name">IHotelService</span></span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>amqp<span class="token punctuation">.</span>rabbit<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span></span><span class="token class-name">RabbitListener</span></span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>beans<span class="token punctuation">.</span>factory<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span></span><span class="token class-name">Autowired</span></span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>stereotype<span class="token punctuation">.</span></span><span class="token class-name">Component</span></span><span class="token punctuation">;</span>

<span class="token annotation punctuation">@Component</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">HotelListener</span> <span class="token punctuation">&#123;</span>

    <span class="token annotation punctuation">@Autowired</span>
    <span class="token keyword">private</span> <span class="token class-name">IHotelService</span> hotelService<span class="token punctuation">;</span>

    <span class="token comment">/**
     * 监听酒店新增或修改的业务
     * @param id 酒店id
     */</span>
    <span class="token annotation punctuation">@RabbitListener</span><span class="token punctuation">(</span>queues <span class="token operator">=</span> <span class="token class-name">MqConstants</span><span class="token punctuation">.</span><span class="token constant">HOTEL_INSERT_QUEUE</span><span class="token punctuation">)</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">listenHotelInsertOrUpdate</span><span class="token punctuation">(</span><span class="token class-name">Long</span> id<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>
        hotelService<span class="token punctuation">.</span><span class="token function">insertById</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>

    <span class="token comment">/**
     * 监听酒店删除的业务
     * @param id 酒店id
     */</span>
    <span class="token annotation punctuation">@RabbitListener</span><span class="token punctuation">(</span>queues <span class="token operator">=</span> <span class="token class-name">MqConstants</span><span class="token punctuation">.</span><span class="token constant">HOTEL_DELETE_QUEUE</span><span class="token punctuation">)</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">listenHotelDelete</span><span class="token punctuation">(</span><span class="token class-name">Long</span> id<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>
        hotelService<span class="token punctuation">.</span><span class="token function">deleteById</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>





<h1 id="4-集群"><a href="#4-集群" class="headerlink" title="4.集群"></a>4.集群</h1><p>单机的elasticsearch做数据存储，必然面临两个问题：海量数据存储问题、单点故障问题。</p>
<ul>
<li>海量数据存储问题：将索引库从逻辑上拆分为N个分片（shard），存储到多个节点</li>
<li>单点故障问题：将分片数据在不同节点备份（replica ）</li>
</ul>
<p><strong>ES集群相关概念</strong>:</p>
<ul>
<li><p>集群（cluster）：一组拥有共同的 cluster name 的 节点。</p>
</li>
<li><p><font color="red">节点（node)</font>   ：集群中的一个 Elasticearch 实例</p>
</li>
<li><p><font color="red">分片（shard）</font>：索引可以被拆分为不同的部分进行存储，称为分片。在集群环境下，一个索引的不同分片可以拆分到不同的节点中</p>
<p>解决问题：数据量太大，单点存储量有限的问题。</p>
<p><img src="assets/image-20200104124440086-5602723.png" alt="image-20200104124440086"></p>
<blockquote>
<p>此处，我们把数据分成3片：shard0、shard1、shard2</p>
</blockquote>
</li>
<li><p>主分片（Primary shard）：相对于副本分片的定义。</p>
</li>
<li><p>副本分片（Replica shard）每个主分片可以有一个或者多个副本，数据和主分片一样。</p>
<p>​    </p>
</li>
</ul>
<p>数据备份可以保证高可用，但是每个分片备份一份，所需要的节点数量就会翻一倍，成本实在是太高了！</p>
<p>为了在高可用和成本间寻求平衡，我们可以这样做：</p>
<ul>
<li>首先对数据分片，存储到不同节点</li>
<li>然后对每个分片进行备份，放到对方节点，完成互相备份</li>
</ul>
<p>这样可以大大减少所需要的服务节点数量，如图，我们以3分片，每个分片备份一份为例：</p>
<p><img src="assets/image-20200104124551912.png" alt="image-20200104124551912"></p>
<p>现在，每个分片都有1个备份，存储在3个节点：</p>
<ul>
<li>node0：保存了分片0和1</li>
<li>node1：保存了分片0和2</li>
<li>node2：保存了分片1和2</li>
</ul>
<h2 id="4-1-搭建ES集群"><a href="#4-1-搭建ES集群" class="headerlink" title="4.1.搭建ES集群"></a>4.1.搭建ES集群</h2><p>参考课前资料的文档：</p>
<p><img src="assets/image-20210723222732427.png" alt="image-20210723222732427"> </p>
<p>其中的第四章节：</p>
<p><img src="assets/image-20210723222812619.png" alt="image-20210723222812619"> </p>
<h2 id="4-2-集群脑裂问题"><a href="#4-2-集群脑裂问题" class="headerlink" title="4.2.集群脑裂问题"></a>4.2.集群脑裂问题</h2><h3 id="4-2-1-集群职责划分"><a href="#4-2-1-集群职责划分" class="headerlink" title="4.2.1.集群职责划分"></a>4.2.1.集群职责划分</h3><p>elasticsearch中集群节点有不同的职责划分：</p>
<p><img src="assets/image-20210723223008967.png" alt="image-20210723223008967"></p>
<p>默认情况下，集群中的任何一个节点都同时具备上述四种角色。</p>
<p>但是真实的集群一定要将集群职责分离：</p>
<ul>
<li>master节点：对CPU要求高，但是内存要求第</li>
<li>data节点：对CPU和内存要求都高</li>
<li>coordinating节点：对网络带宽、CPU要求高</li>
</ul>
<p>职责分离可以让我们根据不同节点的需求分配不同的硬件去部署。而且避免业务之间的互相干扰。</p>
<p>一个典型的es集群职责划分如图：</p>
<p><img src="assets/image-20210723223629142.png" alt="image-20210723223629142"></p>
<h3 id="4-2-2-脑裂问题"><a href="#4-2-2-脑裂问题" class="headerlink" title="4.2.2.脑裂问题"></a>4.2.2.脑裂问题</h3><p>脑裂是因为集群中的节点失联导致的。</p>
<p>例如一个集群中，主节点与其它节点失联：</p>
<p><img src="assets/image-20210723223804995.png" alt="image-20210723223804995"></p>
<p>此时，node2和node3认为node1宕机，就会重新选主：</p>
<p><img src="assets/image-20210723223845754.png" alt="image-20210723223845754"></p>
<p>当node3当选后，集群继续对外提供服务，node2和node3自成集群，node1自成集群，两个集群数据不同步，出现数据差异。</p>
<p>当网络恢复后，因为集群中有两个master节点，集群状态的不一致，出现脑裂的情况：</p>
<p><img src="assets/image-20210723224000555.png" alt="image-20210723224000555"></p>
<p>解决脑裂的方案是，要求选票超过 ( eligible节点数量 + 1 ）/ 2 才能当选为主，因此eligible节点数量最好是奇数。对应配置项是discovery.zen.minimum_master_nodes，在es7.0以后，已经成为默认配置，因此一般不会发生脑裂问题</p>
<p>例如：3个节点形成的集群，选票必须超过 （3 + 1） / 2 ，也就是2票。node3得到node2和node3的选票，当选为主。node1只有自己1票，没有当选。集群中依然只有1个主节点，没有出现脑裂。</p>
<h3 id="4-2-3-小结"><a href="#4-2-3-小结" class="headerlink" title="4.2.3.小结"></a>4.2.3.小结</h3><p>master eligible节点的作用是什么？</p>
<ul>
<li>参与集群选主</li>
<li>主节点可以管理集群状态、管理分片信息、处理创建和删除索引库的请求</li>
</ul>
<p>data节点的作用是什么？</p>
<ul>
<li>数据的CRUD</li>
</ul>
<p>coordinator节点的作用是什么？</p>
<ul>
<li><p>路由请求到其它节点</p>
</li>
<li><p>合并查询到的结果，返回给用户</p>
</li>
</ul>
<h2 id="4-3-集群分布式存储"><a href="#4-3-集群分布式存储" class="headerlink" title="4.3.集群分布式存储"></a>4.3.集群分布式存储</h2><p>当新增文档时，应该保存到不同分片，保证数据均衡，那么coordinating node如何确定数据该存储到哪个分片呢？</p>
<h3 id="4-3-1-分片存储测试"><a href="#4-3-1-分片存储测试" class="headerlink" title="4.3.1.分片存储测试"></a>4.3.1.分片存储测试</h3><p>插入三条数据：</p>
<p><img src="assets/image-20210723225006058.png" alt="image-20210723225006058"></p>
<p><img src="assets/image-20210723225034637.png" alt="image-20210723225034637"></p>
<p><img src="assets/image-20210723225112029.png" alt="image-20210723225112029"></p>
<p>测试可以看到，三条数据分别在不同分片：</p>
<p><img src="assets/image-20210723225227928.png" alt="image-20210723225227928"></p>
<p>结果：</p>
<p><img src="assets/image-20210723225342120.png" alt="image-20210723225342120"></p>
<h3 id="4-3-2-分片存储原理"><a href="#4-3-2-分片存储原理" class="headerlink" title="4.3.2.分片存储原理"></a>4.3.2.分片存储原理</h3><p>elasticsearch会通过hash算法来计算文档应该存储到哪个分片：</p>
<p><img src="assets/image-20210723224354904.png" alt="image-20210723224354904"></p>
<p>说明：</p>
<ul>
<li>_routing默认是文档的id</li>
<li>算法与分片数量有关，因此索引库一旦创建，分片数量不能修改！</li>
</ul>
<p>新增文档的流程如下：</p>
<p><img src="assets/image-20210723225436084.png" alt="image-20210723225436084"></p>
<p>解读：</p>
<ul>
<li>1）新增一个id=1的文档</li>
<li>2）对id做hash运算，假如得到的是2，则应该存储到shard-2</li>
<li>3）shard-2的主分片在node3节点，将数据路由到node3</li>
<li>4）保存文档</li>
<li>5）同步给shard-2的副本replica-2，在node2节点</li>
<li>6）返回结果给coordinating-node节点</li>
</ul>
<h2 id="4-4-集群分布式查询"><a href="#4-4-集群分布式查询" class="headerlink" title="4.4.集群分布式查询"></a>4.4.集群分布式查询</h2><p>elasticsearch的查询分成两个阶段：</p>
<ul>
<li><p>scatter phase：分散阶段，coordinating node会把请求分发到每一个分片</p>
</li>
<li><p>gather phase：聚集阶段，coordinating node汇总data node的搜索结果，并处理为最终结果集返回给用户</p>
</li>
</ul>
<p><img src="assets/image-20210723225809848.png" alt="image-20210723225809848"></p>
<h2 id="4-5-集群故障转移"><a href="#4-5-集群故障转移" class="headerlink" title="4.5.集群故障转移"></a>4.5.集群故障转移</h2><p>集群的master节点会监控集群中的节点状态，如果发现有节点宕机，会立即将宕机节点的分片数据迁移到其它节点，确保数据安全，这个叫做故障转移。</p>
<p>1）例如一个集群结构如图：</p>
<p><img src="assets/image-20210723225945963.png" alt="image-20210723225945963"></p>
<p>现在，node1是主节点，其它两个节点是从节点。</p>
<p>2）突然，node1发生了故障：</p>
<p><img src="assets/image-20210723230020574.png" alt="image-20210723230020574"></p>
<p>宕机后的第一件事，需要重新选主，例如选中了node2：</p>
<p><img src="assets/image-20210723230055974.png" alt="image-20210723230055974"></p>
<p>node2成为主节点后，会检测集群监控状态，发现：shard-1、shard-0没有副本节点。因此需要将node1上的数据迁移到node2、node3：</p>
<p><img src="assets/image-20210723230216642.png" alt="image-20210723230216642"></p>

                
            </div>
            <hr/>

            

    <div class="reprint" id="reprint-statement">
        
            <div class="reprint__author">
                <span class="reprint-meta" style="font-weight: bold;">
                    <i class="fas fa-user">
                        Author:
                    </i>
                </span>
                <span class="reprint-info">
                    <a href="/about" rel="external nofollow noreferrer">凡诚</a>
                </span>
            </div>
            <div class="reprint__type">
                <span class="reprint-meta" style="font-weight: bold;">
                    <i class="fas fa-link">
                        Link:
                    </i>
                </span>
                <span class="reprint-info">
                    <a href="http://example.com/2022/08/29/fen-bu-shi-sou-suo-yin-qing-xia/">http://example.com/2022/08/29/fen-bu-shi-sou-suo-yin-qing-xia/</a>
                </span>
            </div>
            <div class="reprint__notice">
                <span class="reprint-meta" style="font-weight: bold;">
                    <i class="fas fa-copyright">
                        Reprint policy:
                    </i>
                </span>
                <span class="reprint-info">
                    All articles in this blog are used except for special statements
                    <a href="https://creativecommons.org/licenses/by/4.0/deed.zh" rel="external nofollow noreferrer" target="_blank">CC BY 4.0</a>
                    reprint polocy. If reproduced, please indicate source
                    <a href="/about" target="_blank">凡诚</a>
                    !
                </span>
            </div>
        
    </div>

    <script async defer>
      document.addEventListener("copy", function (e) {
        let toastHTML = '<span>Copied successfully, please follow the reprint policy of this article</span><button class="btn-flat toast-action" onclick="navToReprintStatement()" style="font-size: smaller">more</a>';
        M.toast({html: toastHTML})
      });

      function navToReprintStatement() {
        $("html, body").animate({scrollTop: $("#reprint-statement").offset().top - 80}, 800);
      }
    </script>



            <div class="tag_share" style="display: block;">
                <div class="post-meta__tag-list" style="display: inline-block;">
                    
                        <div class="article-tag">
                            
                                <a href="/tags/java/">
                                    <span class="chip bg-color">java</span>
                                </a>
                            
                                <a href="/tags/spring/">
                                    <span class="chip bg-color">spring</span>
                                </a>
                            
                                <a href="/tags/springBoot/">
                                    <span class="chip bg-color">springBoot</span>
                                </a>
                            
                                <a href="/tags/%E5%BE%AE%E6%9C%8D%E5%8A%A1/">
                                    <span class="chip bg-color">微服务</span>
                                </a>
                            
                                <a href="/tags/%E5%88%86%E5%B8%83%E5%BC%8F/">
                                    <span class="chip bg-color">分布式</span>
                                </a>
                            
                                <a href="/tags/springCloud/">
                                    <span class="chip bg-color">springCloud</span>
                                </a>
                            
                                <a href="/tags/elasticSearch/">
                                    <span class="chip bg-color">elasticSearch</span>
                                </a>
                            
                        </div>
                    
                </div>
                <div class="post_share" style="zoom: 80%; width: fit-content; display: inline-block; float: right; margin: -0.15rem 0;">
                    <link rel="stylesheet" type="text/css" href="/libs/share/css/share.min.css">
<div id="article-share">

    
    <div class="social-share" data-sites="twitter,facebook,google,qq,qzone,wechat,weibo,douban,linkedin" data-wechat-qrcode-helper="<p>微信扫一扫即可分享！</p>"></div>
    <script src="/libs/share/js/social-share.min.js"></script>
    

    

</div>

                </div>
            </div>
            
                <style>
    #reward {
        margin: 40px 0;
        text-align: center;
    }

    #reward .reward-link {
        font-size: 1.4rem;
        line-height: 38px;
    }

    #reward .btn-floating:hover {
        box-shadow: 0 6px 12px rgba(0, 0, 0, 0.2), 0 5px 15px rgba(0, 0, 0, 0.2);
    }

    #rewardModal {
        width: 320px;
        height: 350px;
    }

    #rewardModal .reward-title {
        margin: 15px auto;
        padding-bottom: 5px;
    }

    #rewardModal .modal-content {
        padding: 10px;
    }

    #rewardModal .close {
        position: absolute;
        right: 15px;
        top: 15px;
        color: rgba(0, 0, 0, 0.5);
        font-size: 1.3rem;
        line-height: 20px;
        cursor: pointer;
    }

    #rewardModal .close:hover {
        color: #ef5350;
        transform: scale(1.3);
        -moz-transform:scale(1.3);
        -webkit-transform:scale(1.3);
        -o-transform:scale(1.3);
    }

    #rewardModal .reward-tabs {
        margin: 0 auto;
        width: 210px;
    }

    .reward-tabs .tabs {
        height: 38px;
        margin: 10px auto;
        padding-left: 0;
    }

    .reward-content ul {
        padding-left: 0 !important;
    }

    .reward-tabs .tabs .tab {
        height: 38px;
        line-height: 38px;
    }

    .reward-tabs .tab a {
        color: #fff;
        background-color: #ccc;
    }

    .reward-tabs .tab a:hover {
        background-color: #ccc;
        color: #fff;
    }

    .reward-tabs .wechat-tab .active {
        color: #fff !important;
        background-color: #22AB38 !important;
    }

    .reward-tabs .alipay-tab .active {
        color: #fff !important;
        background-color: #019FE8 !important;
    }

    .reward-tabs .reward-img {
        width: 210px;
        height: 210px;
    }
</style>

<div id="reward">
    <a href="#rewardModal" class="reward-link modal-trigger btn-floating btn-medium waves-effect waves-light red">赏</a>

    <!-- Modal Structure -->
    <div id="rewardModal" class="modal">
        <div class="modal-content">
            <a class="close modal-close"><i class="fas fa-times"></i></a>
            <h4 class="reward-title">你的赏识是我前进的动力</h4>
            <div class="reward-content">
                <div class="reward-tabs">
                    <ul class="tabs row">
                        <li class="tab col s6 alipay-tab waves-effect waves-light"><a href="#alipay">支付宝</a></li>
                        <li class="tab col s6 wechat-tab waves-effect waves-light"><a href="#wechat">微 信</a></li>
                    </ul>
                    <div id="alipay">
                        <img src="/medias/reward/alipay.jpg" class="reward-img" alt="支付宝打赏二维码">
                    </div>
                    <div id="wechat">
                        <img src="/medias/reward/wechat.png" class="reward-img" alt="微信打赏二维码">
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

<script>
    $(function () {
        $('.tabs').tabs();
    });
</script>

            
        </div>
    </div>

    

    

    

    
    <div class="livere-card card" data-aos="fade-up">
    <!-- 来必力City版安装代码 -->
    <div id="lv-container" class="card-content" data-id="city" data-uid="MTAyMC81NzU4NC8zNDA0OA==">
        <script type="text/javascript">
            (function (d, s) {
                let j, e = d.getElementsByTagName(s)[0];
                if (typeof LivereTower === 'function') {
                    return;
                }

                j = d.createElement(s);
                j.src = 'https://cdn-city.livere.com/js/embed.dist.js';
                j.async = true;

                e.parentNode.insertBefore(j, e);
            })(document, 'script');
        </script>
        <noscript>为正常使用来必力评论功能请激活JavaScript。</noscript>
    </div>
    <!-- City版安装代码已完成 -->
</div>
    

    

    

    

    

<article id="prenext-posts" class="prev-next articles">
    <div class="row article-row">
        
        <div class="article col s12 m6" data-aos="fade-up">
            <div class="article-badge left-badge text-color">
                <i class="fas fa-chevron-left"></i>&nbsp;Previous</div>
            <div class="card">
                <a href="/2022/08/29/fen-bu-shi-sou-suo-yin-qing-zhong/">
                    <div class="card-image">
                        
                        
                        <img src="/medias/featureimages/8.jpg" class="responsive-img" alt="分布式搜索引擎中">
                        
                        <span class="card-title">分布式搜索引擎中</span>
                    </div>
                </a>
                <div class="card-content article-content">
                    <div class="summary block-with-text">
                        
                            elasticsearch是一款非常强大的开源搜索引擎，具备非常多强大功能，可以帮助我们从海量数据中快速找到需要的内容
                        
                    </div>
                    <div class="publish-info">
                        <span class="publish-date">
                            <i class="far fa-clock fa-fw icon-date"></i>2022-08-29
                        </span>
                        <span class="publish-author">
                            
                            <i class="fas fa-bookmark fa-fw icon-category"></i>
                            
                            <a href="/categories/%E5%BE%AE%E6%9C%8D%E5%8A%A1%E6%8A%80%E6%9C%AF/" class="post-category">
                                    微服务技术
                                </a>
                            
                            
                        </span>
                    </div>
                </div>
                
                <div class="card-action article-tags">
                    
                    <a href="/tags/java/">
                        <span class="chip bg-color">java</span>
                    </a>
                    
                    <a href="/tags/spring/">
                        <span class="chip bg-color">spring</span>
                    </a>
                    
                    <a href="/tags/springBoot/">
                        <span class="chip bg-color">springBoot</span>
                    </a>
                    
                    <a href="/tags/%E5%BE%AE%E6%9C%8D%E5%8A%A1/">
                        <span class="chip bg-color">微服务</span>
                    </a>
                    
                    <a href="/tags/%E5%88%86%E5%B8%83%E5%BC%8F/">
                        <span class="chip bg-color">分布式</span>
                    </a>
                    
                    <a href="/tags/springCloud/">
                        <span class="chip bg-color">springCloud</span>
                    </a>
                    
                    <a href="/tags/elasticSearch/">
                        <span class="chip bg-color">elasticSearch</span>
                    </a>
                    
                </div>
                
            </div>
        </div>
        
        
        <div class="article col s12 m6" data-aos="fade-up">
            <div class="article-badge right-badge text-color">
                Next&nbsp;<i class="fas fa-chevron-right"></i>
            </div>
            <div class="card">
                <a href="/2022/08/29/fen-bu-shi-sou-suo-yin-qing-shang/">
                    <div class="card-image">
                        
                        
                        <img src="/medias/featureimages/3.jpg" class="responsive-img" alt="分布式搜索引擎上">
                        
                        <span class="card-title">分布式搜索引擎上</span>
                    </div>
                </a>
                <div class="card-content article-content">
                    <div class="summary block-with-text">
                        
                            elasticsearch是一款非常强大的开源搜索引擎，具备非常多强大功能，可以帮助我们从海量数据中快速找到需要的内容
                        
                    </div>
                    <div class="publish-info">
                            <span class="publish-date">
                                <i class="far fa-clock fa-fw icon-date"></i>2022-08-29
                            </span>
                        <span class="publish-author">
                            
                            <i class="fas fa-bookmark fa-fw icon-category"></i>
                            
                            <a href="/categories/%E5%BE%AE%E6%9C%8D%E5%8A%A1%E6%8A%80%E6%9C%AF/" class="post-category">
                                    微服务技术
                                </a>
                            
                            
                        </span>
                    </div>
                </div>
                
                <div class="card-action article-tags">
                    
                    <a href="/tags/java/">
                        <span class="chip bg-color">java</span>
                    </a>
                    
                    <a href="/tags/spring/">
                        <span class="chip bg-color">spring</span>
                    </a>
                    
                    <a href="/tags/springBoot/">
                        <span class="chip bg-color">springBoot</span>
                    </a>
                    
                    <a href="/tags/%E5%BE%AE%E6%9C%8D%E5%8A%A1/">
                        <span class="chip bg-color">微服务</span>
                    </a>
                    
                    <a href="/tags/%E5%88%86%E5%B8%83%E5%BC%8F/">
                        <span class="chip bg-color">分布式</span>
                    </a>
                    
                    <a href="/tags/springCloud/">
                        <span class="chip bg-color">springCloud</span>
                    </a>
                    
                    <a href="/tags/elasticSearch/">
                        <span class="chip bg-color">elasticSearch</span>
                    </a>
                    
                </div>
                
            </div>
        </div>
        
    </div>
</article>

</div>



<!-- 代码块功能依赖 -->
<script type="text/javascript" src="/libs/codeBlock/codeBlockFuction.js"></script>

<!-- 代码语言 -->

<script type="text/javascript" src="/libs/codeBlock/codeLang.js"></script>


<!-- 代码块复制 -->

<script type="text/javascript" src="/libs/codeBlock/codeCopy.js"></script>


<!-- 代码块收缩 -->

<script type="text/javascript" src="/libs/codeBlock/codeShrink.js"></script>


    </div>
    <div id="toc-aside" class="expanded col l3 hide-on-med-and-down">
        <div class="toc-widget card" style="background-color: white;">
            <div class="toc-title"><i class="far fa-list-alt"></i>&nbsp;&nbsp;TOC</div>
            <div id="toc-content"></div>
        </div>
    </div>
</div>

<!-- TOC 悬浮按钮. -->

<div id="floating-toc-btn" class="hide-on-med-and-down">
    <a class="btn-floating btn-large bg-color">
        <i class="fas fa-list-ul"></i>
    </a>
</div>


<script src="/libs/tocbot/tocbot.min.js"></script>
<script>
    $(function () {
        tocbot.init({
            tocSelector: '#toc-content',
            contentSelector: '#articleContent',
            headingsOffset: -($(window).height() * 0.4 - 45),
            collapseDepth: Number('0'),
            headingSelector: 'h2, h3, h4'
        });

        // modify the toc link href to support Chinese.
        let i = 0;
        let tocHeading = 'toc-heading-';
        $('#toc-content a').each(function () {
            $(this).attr('href', '#' + tocHeading + (++i));
        });

        // modify the heading title id to support Chinese.
        i = 0;
        $('#articleContent').children('h2, h3, h4').each(function () {
            $(this).attr('id', tocHeading + (++i));
        });

        // Set scroll toc fixed.
        let tocHeight = parseInt($(window).height() * 0.4 - 64);
        let $tocWidget = $('.toc-widget');
        $(window).scroll(function () {
            let scroll = $(window).scrollTop();
            /* add post toc fixed. */
            if (scroll > tocHeight) {
                $tocWidget.addClass('toc-fixed');
            } else {
                $tocWidget.removeClass('toc-fixed');
            }
        });

        
        /* 修复文章卡片 div 的宽度. */
        let fixPostCardWidth = function (srcId, targetId) {
            let srcDiv = $('#' + srcId);
            if (srcDiv.length === 0) {
                return;
            }

            let w = srcDiv.width();
            if (w >= 450) {
                w = w + 21;
            } else if (w >= 350 && w < 450) {
                w = w + 18;
            } else if (w >= 300 && w < 350) {
                w = w + 16;
            } else {
                w = w + 14;
            }
            $('#' + targetId).width(w);
        };

        // 切换TOC目录展开收缩的相关操作.
        const expandedClass = 'expanded';
        let $tocAside = $('#toc-aside');
        let $mainContent = $('#main-content');
        $('#floating-toc-btn .btn-floating').click(function () {
            if ($tocAside.hasClass(expandedClass)) {
                $tocAside.removeClass(expandedClass).hide();
                $mainContent.removeClass('l9');
            } else {
                $tocAside.addClass(expandedClass).show();
                $mainContent.addClass('l9');
            }
            fixPostCardWidth('artDetail', 'prenext-posts');
        });
        
    });
</script>

    

</main>




    <footer class="page-footer bg-color">
    
        <link rel="stylesheet" href="/libs/aplayer/APlayer.min.css">
<style>
    .aplayer .aplayer-lrc p {
        
        font-size: 12px;
        font-weight: 700;
        line-height: 16px !important;
    }

    .aplayer .aplayer-lrc p.aplayer-lrc-current {
        
        font-size: 15px;
        color: blue;
    }

    
    .aplayer.aplayer-fixed.aplayer-narrow .aplayer-body {
        left: -66px !important;
    }

    .aplayer.aplayer-fixed.aplayer-narrow .aplayer-body:hover {
        left: 0px !important;
    }

    
</style>
<div class="">
    
    <div class="row">
        <meting-js class="col l8 offset-l2 m10 offset-m1 s12"
                   server="netease"
                   type="playlist"
                   id="2477330090"
                   fixed='true'
                   autoplay='false'
                   theme='blue'
                   loop='all'
                   order='random'
                   preload='auto'
                   volume='0.7'
                   list-folded='true'
        >
        </meting-js>
    </div>
</div>

<script src="/libs/aplayer/APlayer.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/meting@2/dist/Meting.min.js"></script>

    
    <div class="container row center-align" style="margin-bottom: 15px !important;">
        <div class="col s12 m8 l8 copy-right">
            Copyright&nbsp;&copy;
            
                <span id="year">2022</span>
            
            <span id="year">2022</span>
            <a href="/about" target="_blank">凡诚</a>
            &nbsp;&nbsp;<a href="https://hexo.io/" target="_blank"></a>
            |&nbsp;Theme&nbsp;<a href="https://github.com/blinkfox/hexo-theme-matery" target="_blank">Matery</a>
            <br>
            
            &nbsp;<i class="fas fa-chart-area"></i>&nbsp;站点总字数:&nbsp;<span
                class="white-color">607.1k</span>&nbsp;字
            
            
            
            
            
            
            <span id="busuanzi_container_site_pv">
                |&nbsp;<i class="far fa-eye"></i>&nbsp;总访问量:&nbsp;<span id="busuanzi_value_site_pv"
                    class="white-color"></span>&nbsp;次
            </span>
            
            
            <span id="busuanzi_container_site_uv">
                |&nbsp;<i class="fas fa-users"></i>&nbsp;总访问人数:&nbsp;<span id="busuanzi_value_site_uv"
                    class="white-color"></span>&nbsp;人
            </span>
            
            <br>
            
            <span id="sitetime">载入运行时间...</span>
            <script>
                function siteTime() {
                    var seconds = 1000;
                    var minutes = seconds * 60;
                    var hours = minutes * 60;
                    var days = hours * 24;
                    var years = days * 365;
                    var today = new Date();
                    var startYear = "2022";
                    var startMonth = "11";
                    var startDate = "7";
                    var startHour = "0";
                    var startMinute = "0";
                    var startSecond = "0";
                    var todayYear = today.getFullYear();
                    var todayMonth = today.getMonth() + 1;
                    var todayDate = today.getDate();
                    var todayHour = today.getHours();
                    var todayMinute = today.getMinutes();
                    var todaySecond = today.getSeconds();
                    var t1 = Date.UTC(startYear, startMonth, startDate, startHour, startMinute, startSecond);
                    var t2 = Date.UTC(todayYear, todayMonth, todayDate, todayHour, todayMinute, todaySecond);
                    var diff = t2 - t1;
                    var diffYears = Math.floor(diff / years);
                    var diffDays = Math.floor((diff / days) - diffYears * 365);
                    var diffHours = Math.floor((diff - (diffYears * 365 + diffDays) * days) / hours);
                    var diffMinutes = Math.floor((diff - (diffYears * 365 + diffDays) * days - diffHours * hours) /
                        minutes);
                    var diffSeconds = Math.floor((diff - (diffYears * 365 + diffDays) * days - diffHours * hours -
                        diffMinutes * minutes) / seconds);
                    if (startYear == todayYear) {
                        document.getElementById("year").innerHTML = todayYear;
                        document.getElementById("sitetime").innerHTML = "本站已安全运行 " + diffDays + " 天 " + diffHours +
                            " 小时 " + diffMinutes + " 分钟 " + diffSeconds + " 秒";
                    } else {
                        document.getElementById("year").innerHTML = startYear + " - " + todayYear;
                        document.getElementById("sitetime").innerHTML = "本站已安全运行 " + diffYears + " 年 " + diffDays +
                            " 天 " + diffHours + " 小时 " + diffMinutes + " 分钟 " + diffSeconds + " 秒";
                    }
                }
                setInterval(siteTime, 1000);
            </script>
            
            <br>
            
        </div>
        <div class="col s12 m4 l4 social-link social-statis">
    <a href="https://github.com/fanshicheng" class="tooltipped" target="_blank" data-tooltip="访问我的GitHub" data-position="top" data-delay="50">
        <i class="fab fa-github"></i>
    </a>



    <a href="mailto:2639144944@qq.com" class="tooltipped" target="_blank" data-tooltip="邮件联系我" data-position="top" data-delay="50">
        <i class="fas fa-envelope-open"></i>
    </a>







    <a href="tencent://AddContact/?fromId=50&fromSubId=1&subcmd=all&uin=2639144944" class="tooltipped" target="_blank" data-tooltip="QQ联系我: 2639144944" data-position="top" data-delay="50">
        <i class="fab fa-qq"></i>
    </a>







</div>
    </div>
</footer>

<div class="progress-bar"></div>


    <!-- 搜索遮罩框 -->
<div id="searchModal" class="modal">
    <div class="modal-content">
        <div class="search-header">
            <span class="title"><i class="fas fa-search"></i>&nbsp;&nbsp;Search</span>
            <input type="search" id="searchInput" name="s" placeholder="Please enter a search keyword"
                   class="search-input">
        </div>
        <div id="searchResult"></div>
    </div>
</div>

<script type="text/javascript">
$(function () {
    var searchFunc = function (path, search_id, content_id) {
        'use strict';
        $.ajax({
            url: path,
            dataType: "xml",
            success: function (xmlResponse) {
                // get the contents from search data
                var datas = $("entry", xmlResponse).map(function () {
                    return {
                        title: $("title", this).text(),
                        content: $("content", this).text(),
                        url: $("url", this).text()
                    };
                }).get();
                var $input = document.getElementById(search_id);
                var $resultContent = document.getElementById(content_id);
                $input.addEventListener('input', function () {
                    var str = '<ul class=\"search-result-list\">';
                    var keywords = this.value.trim().toLowerCase().split(/[\s\-]+/);
                    $resultContent.innerHTML = "";
                    if (this.value.trim().length <= 0) {
                        return;
                    }
                    // perform local searching
                    datas.forEach(function (data) {
                        var isMatch = true;
                        var data_title = data.title.trim().toLowerCase();
                        var data_content = data.content.trim().replace(/<[^>]+>/g, "").toLowerCase();
                        var data_url = data.url;
                        data_url = data_url.indexOf('/') === 0 ? data.url : '/' + data_url;
                        var index_title = -1;
                        var index_content = -1;
                        var first_occur = -1;
                        // only match artiles with not empty titles and contents
                        if (data_title !== '' && data_content !== '') {
                            keywords.forEach(function (keyword, i) {
                                index_title = data_title.indexOf(keyword);
                                index_content = data_content.indexOf(keyword);
                                if (index_title < 0 && index_content < 0) {
                                    isMatch = false;
                                } else {
                                    if (index_content < 0) {
                                        index_content = 0;
                                    }
                                    if (i === 0) {
                                        first_occur = index_content;
                                    }
                                }
                            });
                        }
                        // show search results
                        if (isMatch) {
                            str += "<li><a href='" + data_url + "' class='search-result-title'>" + data_title + "</a>";
                            var content = data.content.trim().replace(/<[^>]+>/g, "");
                            if (first_occur >= 0) {
                                // cut out 100 characters
                                var start = first_occur - 20;
                                var end = first_occur + 80;
                                if (start < 0) {
                                    start = 0;
                                }
                                if (start === 0) {
                                    end = 100;
                                }
                                if (end > content.length) {
                                    end = content.length;
                                }
                                var match_content = content.substr(start, end);
                                // highlight all keywords
                                keywords.forEach(function (keyword) {
                                    var regS = new RegExp(keyword, "gi");
                                    match_content = match_content.replace(regS, "<em class=\"search-keyword\">" + keyword + "</em>");
                                });

                                str += "<p class=\"search-result\">" + match_content + "...</p>"
                            }
                            str += "</li>";
                        }
                    });
                    str += "</ul>";
                    $resultContent.innerHTML = str;
                });
            }
        });
    };

    searchFunc('/search.xml', 'searchInput', 'searchResult');
});
</script>

    <!-- 回到顶部按钮 -->
<div id="backTop" class="top-scroll">
    <a class="btn-floating btn-large waves-effect waves-light" href="#!">
        <i class="fas fa-arrow-up"></i>
    </a>
</div>

    <script type="text/javascript">
        var OriginTitile=document.title,st;
        document.addEventListener("visibilitychange",function(){
            document.hidden?(document.title="(Ő∀Ő3)ノ",clearTimeout(st)):(document.title="ヽ(●-`Д´-)ノ欢迎回来",st=setTimeout(function(){document.title=OriginTitile},3e3))
        })
    </script>
    <!-- <script src="/js/cursor.js"></script> -->
    <script type="text/javascript">
        //只在桌面版网页启用特效
        var windowWidth = $(window).width();
        if (windowWidth > 768) {
            document.write('<script type="text/javascript" src="/js/sakura.js"><\/script>');
        }
        </script>
    <script src="/libs/materialize/materialize.min.js"></script>
    <script src="/libs/masonry/masonry.pkgd.min.js"></script>
    <script src="/libs/aos/aos.js"></script>
    <script src="/libs/scrollprogress/scrollProgress.min.js"></script>
    <script src="/libs/lightGallery/js/lightgallery-all.min.js"></script>
    <script src="/js/matery.js"></script>

    <!-- Baidu Analytics -->

    <!-- Baidu Push -->

<script>
    (function () {
        var bp = document.createElement('script');
        var curProtocol = window.location.protocol.split(':')[0];
        if (curProtocol === 'https') {
            bp.src = 'https://zz.bdstatic.com/linksubmit/push.js';
        } else {
            bp.src = 'http://push.zhanzhang.baidu.com/push.js';
        }
        var s = document.getElementsByTagName("script")[0];
        s.parentNode.insertBefore(bp, s);
    })();
</script>

    
    
    <script async src="/libs/others/busuanzi.pure.mini.js"></script>
    

    

    
    <script>
        (function (i, s, o, g, r, a, m) {
            i["DaoVoiceObject"] = r;
            i[r] = i[r] || function () {
                (i[r].q = i[r].q || []).push(arguments)
            }, i[r].l = 1 * new Date();
            a = s.createElement(o), m = s.getElementsByTagName(o)[0];
            a.async = 1;
            a.src = g;
            a.charset = "utf-8";
            m.parentNode.insertBefore(a, m)
        })(window, document, "script", ('https:' == document.location.protocol ? 'https:' : 'http:') +
            "//widget.daovoice.io/widget/6984b559.js", "daovoice")
        daovoice('init', {
            app_id: "377cb7de"
        });
        daovoice('update');
    </script>
    

	
    
    <script type="text/javascript" color="0,0,255"
        pointColor="0,0,255" opacity='0.7'
        zIndex="-1" count="99"
        src="/libs/background/canvas-nest.js"></script>
    

    

    

    
    <script src="/libs/instantpage/instantpage.js" type="module"></script>
    
    <!-- 冒泡 -->
    
<canvas class="fireworks" style="position: fixed;left: 0;top: 0;z-index: 1; pointer-events: none;" ></canvas> 
<script type="text/javascript" src="//cdn.bootcss.com/animejs/2.2.0/anime.min.js"></script> 
<script type="text/javascript" src="/js/fireworks.js"></script>


</body>

</html>
